visual-journal / scripts /patch-standalone-runtime.test.mjs
gpt-image-playground deploy
Deploy 17eadcc to Docker Space
96bdf6c
Raw
History Blame Contribute Delete
7.4 kB
import assert from 'node:assert/strict';
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { after, before, describe, it } from 'node:test';
import { patchStandaloneRuntime, validateStandaloneSharpRuntime } from './patch-standalone-runtime.mjs';
const tempDirectories = [];
const originalConsoleInfo = console.info;
before(() => {
console.info = () => {};
});
after(() => {
console.info = originalConsoleInfo;
for (const directory of tempDirectories) {
rmSync(directory, { recursive: true, force: true, maxRetries: 3 });
}
});
describe('standalone runtime patch', () => {
it('把完整 sharp 运行文件复制到 standalone 和 Turbopack 外部目录', async () => {
const root = createTempRepo();
await writeFakeSharpPackage(root);
await writeFile(join(root, 'node_modules/semver/index.js'), 'module.exports = {};\n');
await writeFile(join(root, 'node_modules/semver/package.json'), '{"name":"semver"}\n');
await writeFile(join(root, 'node_modules/detect-libc/lib/detect-libc.js'), 'module.exports = {};\n');
await writeFile(join(root, 'node_modules/detect-libc/package.json'), '{"name":"detect-libc"}\n');
await writeFile(join(root, 'node_modules/@img/colour/index.cjs'), 'module.exports = {};\n');
await writeFile(join(root, 'node_modules/@img/colour/package.json'), '{"name":"@img/colour"}\n');
await writeFile(join(root, 'node_modules/@img/sharp-linuxmusl-x64/package.json'), '{"name":"@img/sharp-linuxmusl-x64"}\n');
await writeFile(join(root, 'node_modules/@img/sharp-libvips-linuxmusl-x64/package.json'), '{"name":"@img/sharp-libvips-linuxmusl-x64"}\n');
await writeFile(join(root, 'node_modules/@img/not-sharp/package.json'), '{"name":"@img/not-sharp"}\n');
await writeFile(join(root, '.next/node_modules/sharp-hashed/package.json'), '{"name":"sharp"}\n');
await writeFile(join(root, '.next/node_modules/sharp-unrelated/package.json'), '{"name":"sharp-unrelated"}\n');
await patchStandaloneRuntime(root);
assert.equal(existsSync(join(root, '.next/standalone/node_modules/sharp/dist/index.mjs')), true);
assert.equal(existsSync(join(root, '.next/standalone/node_modules/sharp/dist/index.cjs')), true);
assert.equal(existsSync(join(root, '.next/standalone/node_modules/semver/index.js')), true);
assert.equal(existsSync(join(root, '.next/standalone/node_modules/detect-libc/lib/detect-libc.js')), true);
assert.equal(existsSync(join(root, '.next/standalone/node_modules/@img/colour/index.cjs')), true);
assert.equal(existsSync(join(root, '.next/standalone/node_modules/@img/sharp-linuxmusl-x64/package.json')), true);
assert.equal(existsSync(join(root, '.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/package.json')), true);
assert.equal(existsSync(join(root, '.next/standalone/node_modules/@img/not-sharp/package.json')), false);
assert.equal(existsSync(join(root, '.next/standalone/.next/node_modules/sharp-hashed/dist/index.mjs')), true);
assert.equal(existsSync(join(root, '.next/standalone/.next/node_modules/sharp-unrelated/dist/index.mjs')), false);
assert.equal(
readFileSync(join(root, '.next/standalone/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.prod.js'), 'utf8'),
'runtime\n'
);
});
it('在 standalone 布局下真实加载 sharp 并运行自检', async () => {
const root = createTempRepo();
await writeFakeSharpPackage(root);
await patchStandaloneRuntime(root);
await validateStandaloneSharpRuntime(root);
});
it('standalone sharp 运行时无法加载时显式失败', async () => {
const root = createTempRepo();
await writeFakeSharpPackage(root);
await patchStandaloneRuntime(root);
await writeFile(join(root, '.next/standalone/node_modules/sharp/dist/index.cjs'), 'throw new Error("broken sharp");\n');
await assert.rejects(
() => validateStandaloneSharpRuntime(root),
/standalone sharp 运行时加载失败/
);
});
it('缺少 sharp 核心运行包时显式失败', async () => {
const root = createTempRepo();
await writeFakeSharpPackage(root);
rmSync(join(root, 'node_modules/semver'), { force: true, recursive: true });
await assert.rejects(
() => patchStandaloneRuntime(root),
/standalone sharp 运行依赖缺失/
);
});
it('缺少 sharp native binding 平台包时显式失败', async () => {
const root = createTempRepo();
await writeFakeSharpPackage(root);
rmSync(join(root, 'node_modules/@img/sharp-linuxmusl-x64'), { force: true, recursive: true });
await assert.rejects(
() => patchStandaloneRuntime(root),
/standalone sharp native binding 依赖缺失/
);
});
it('缺少 sharp libvips 平台包时显式失败', async () => {
const root = createTempRepo();
await writeFakeSharpPackage(root);
rmSync(join(root, 'node_modules/@img/sharp-libvips-linuxmusl-x64'), { force: true, recursive: true });
await assert.rejects(
() => patchStandaloneRuntime(root),
/standalone sharp libvips 依赖缺失/
);
});
it('缺少 sharp colour 平台包时显式失败', async () => {
const root = createTempRepo();
await writeFakeSharpPackage(root);
rmSync(join(root, 'node_modules/@img/colour'), { force: true, recursive: true });
await assert.rejects(
() => patchStandaloneRuntime(root),
/standalone sharp colour 依赖缺失/
);
});
});
function createTempRepo() {
const root = mkdtempSync(join(tmpdir(), 'standalone-runtime-test-'));
tempDirectories.push(root);
const runtimeDir = join(root, 'node_modules/next/dist/compiled/next-server');
mkdirSyncRecursive(runtimeDir);
mkdirSyncRecursive(join(root, 'node_modules/sharp/dist'));
mkdirSyncRecursive(join(root, 'node_modules/semver'));
mkdirSyncRecursive(join(root, 'node_modules/detect-libc/lib'));
mkdirSyncRecursive(join(root, 'node_modules/@img/colour'));
mkdirSyncRecursive(join(root, 'node_modules/@img/sharp-linuxmusl-x64'));
mkdirSyncRecursive(join(root, 'node_modules/@img/sharp-libvips-linuxmusl-x64'));
mkdirSyncRecursive(join(root, 'node_modules/@img/not-sharp'));
mkdirSyncRecursive(join(root, '.next/node_modules/sharp-hashed'));
mkdirSyncRecursive(join(root, '.next/node_modules/sharp-unrelated'));
mkdirSyncRecursive(join(root, '.next/standalone'));
writeFileSync(join(runtimeDir, 'app-route-turbo.runtime.prod.js'), 'runtime\n');
writeFileSync(join(runtimeDir, 'app-route-turbo.runtime.prod.js.map'), '{}\n');
return root;
}
async function writeFakeSharpPackage(root) {
const fakeSharp = `
function sharp() {
return {
webp() {
return this;
},
async toBuffer() {
return Buffer.from('fake-webp');
}
};
}
module.exports = sharp;
`;
await writeFile(join(root, 'node_modules/sharp/dist/index.mjs'), 'export default function sharp() {}\n');
await writeFile(join(root, 'node_modules/sharp/dist/index.cjs'), fakeSharp);
await writeFile(join(root, 'node_modules/sharp/package.json'), '{"name":"sharp","main":"dist/index.cjs"}\n');
}
function mkdirSyncRecursive(directory) {
rmSync(directory, { force: true, recursive: true });
mkdirSync(directory, { recursive: true });
}