61 lines
4.2 KiB
JavaScript
61 lines
4.2 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { mkdtemp, readFile, realpath, rm, writeFile } from 'node:fs/promises';
|
|
import { tmpdir } from 'node:os';
|
|
import { dirname, join, resolve } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const root=resolve(dirname(fileURLToPath(import.meta.url)),'../..');
|
|
const temporary=await realpath(await mkdtemp(join(tmpdir(),'endoleap-headless-package-')));
|
|
const run=(program,args,options={})=>execFileSync(program,args,{cwd:root,encoding:'utf8',stdio:['ignore','pipe','pipe'],...options});
|
|
try {
|
|
run(process.execPath,['scripts/build-headless.mjs']);
|
|
const packed=JSON.parse(run('npm',['pack','--offline','--ignore-scripts','--json','--pack-destination',temporary],{cwd:join(root,'.headless-dist'),env:{...process.env,npm_config_cache:join(temporary,'npm-cache')}}));
|
|
assert.equal(packed.length,1);
|
|
run('tar',['-xzf',join(temporary,packed[0].filename),'-C',temporary]);
|
|
const packageRoot=join(temporary,'package');
|
|
const metadata=JSON.parse(await readFile(join(packageRoot,'package.json'),'utf8'));
|
|
assert.equal(metadata.private,true);assert.equal(metadata.type,'module');
|
|
assert.equal(Object.keys(metadata.dependencies??{}).length,0);
|
|
assert.equal(Object.keys(metadata.peerDependencies??{}).length,0);
|
|
// Resolve every import through the packed artifact, never checkout node_modules.
|
|
await writeFile(join(temporary,'guard-loader.mjs'),`
|
|
import {fileURLToPath} from 'node:url';
|
|
import {sep} from 'node:path';
|
|
const root=${JSON.stringify(packageRoot)};
|
|
export async function resolve(specifier,context,next){
|
|
if(specifier.startsWith('node:'))return next(specifier,context);
|
|
const result=await next(specifier,context);
|
|
if(!result.url.startsWith('file:')||!fileURLToPath(result.url).startsWith(root+sep))throw Error('External dependency: '+specifier);
|
|
return result;
|
|
}
|
|
`);
|
|
await writeFile(join(temporary,'guard.mjs'),`
|
|
import {register} from 'node:module';
|
|
register('./guard-loader.mjs',import.meta.url);
|
|
for(const name of ['window','document','navigator','indexedDB','Worker'])Object.defineProperty(globalThis,name,{configurable:true,get(){throw Error('Browser global: '+name);}});
|
|
`);
|
|
const report=JSON.parse(run(process.execPath,['--import',join(temporary,'guard.mjs'),join(packageRoot,'consumer.mjs')],{cwd:temporary}));
|
|
assert.equal(report.status,'ok');assert.equal(report.reopened,true);assert.equal(report.elements,2);
|
|
// No DOM library: browser adapter declarations must not leak into portable ports.
|
|
await writeFile(join(packageRoot,'consumer-types.mts'),`
|
|
import {createRegistry,builtinComponents,createDocument} from '@endoleap/headless-canvas/core';
|
|
import {openProjectEngine,type StoragePort,type StorageScope} from '@endoleap/headless-canvas/runtime';
|
|
import {createFoundryPersistence,type ServiceTransport} from '@endoleap/headless-canvas/service';
|
|
const registry=createRegistry(builtinComponents());
|
|
export function open(scope:StorageScope,storage:StoragePort,transport:ServiceTransport){
|
|
return openProjectEngine({scope,storage,registry,document:createDocument(scope.projectId,[{id:scope.projectId,parent:null}],[]),versions:new Map([[scope.projectId,1]]),persistence:createFoundryPersistence({registry,transport,rootId:scope.projectId,workspaceId:scope.workspaceId})});
|
|
}
|
|
`);
|
|
run(process.execPath,[join(root,'node_modules/typescript/bin/tsc'),'--noEmit','--strict','--target','ES2022','--module','NodeNext','--moduleResolution','NodeNext','--lib','ES2022','--types','node','--typeRoots',join(root,'node_modules/@types'),join(packageRoot,'consumer-types.mts')],{cwd:temporary});
|
|
const browser=run(process.execPath,['tests/headless/browser-storage.mjs','--package']);
|
|
process.stdout.write(browser);
|
|
console.log(JSON.stringify({compiledBrowserAdapters:true,status:'ok',packedFiles:packed[0].files.length,consumer:report,nodeTypes:true,browserGlobalsBlocked:true,externalRuntimeDependenciesBlocked:true}));
|
|
} catch(error){
|
|
if(error.stdout)process.stderr.write(error.stdout);
|
|
if(error.stderr)process.stderr.write(error.stderr);
|
|
throw error;
|
|
} finally {
|
|
await rm(temporary,{recursive:true,force:true});
|
|
}
|