chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
import { existsSync, statSync } from 'node:fs';
|
||||
import { cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
||||
import { createRequire } from 'node:module';
|
||||
import { basename, dirname, extname, isAbsolute, relative, resolve } from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { rollup } from '@internal/utils/dts-rollup.js';
|
||||
import {
|
||||
readJsonFile,
|
||||
readPackageJson,
|
||||
workspaceRoot,
|
||||
listFiles,
|
||||
resolvePackagePath,
|
||||
resolvePackageRoot,
|
||||
resolveWorkspacePath,
|
||||
runCommandOrExit,
|
||||
toPosixPath,
|
||||
} from './package-utils.mjs';
|
||||
|
||||
const packageRoot = resolvePackageRoot();
|
||||
const packageJson = readPackageJson(packageRoot);
|
||||
const packageName = packageJson.name ?? packageRoot;
|
||||
const packageRequire = createRequire(resolve(packageRoot, 'package.json'));
|
||||
const { build } = await importPackageDependency('esbuild');
|
||||
|
||||
const srcEntries = [resolvePackagePath(packageRoot, 'src/index.ts', 'source entry')];
|
||||
const runtimeDir = resolvePackagePath(packageRoot, 'dist', 'runtime output');
|
||||
const declarationOutput = resolvePackagePath(packageRoot, 'dist/index.d.ts', 'declaration output');
|
||||
const declarationDir = dirname(declarationOutput);
|
||||
const tempTypesDir = resolve(packageRoot, 'build');
|
||||
const egsPackagesRoot = resolveWorkspacePath('external/egs-core/packages', 'EGS packages root');
|
||||
const workerBundles = [
|
||||
createWorkerBundle({
|
||||
entry: 'external/egs-core/packages/loaders/splat-loader/worker.ts',
|
||||
entryLabel: 'splat worker entry',
|
||||
fileName: 'splat-worker.js',
|
||||
factorySourceFilter: /loaders[\\/]splat-loader[\\/]index\.ts$/,
|
||||
factoryName: 'WorkerFactor',
|
||||
blobUrlName: 'SplatWorkerBlobUrl',
|
||||
nextStatement: 'const poll =',
|
||||
}),
|
||||
createWorkerBundle({
|
||||
entry: 'external/egs-core/packages/loaders/texture-loader/ktx2/basis/worker/transcoder.worker.ts',
|
||||
entryLabel: 'texture transcoder worker entry',
|
||||
fileName: 'transcoder-worker.js',
|
||||
factorySourceFilter: /loaders[\\/]texture-loader[\\/]ktx2[\\/]basis[\\/]worker[\\/]pool\.ts$/,
|
||||
factoryName: 'WorkerFactor',
|
||||
blobUrlName: 'TranscoderWorkerBlobUrl',
|
||||
nextStatement: 'const pool =',
|
||||
}),
|
||||
createWorkerBundle({
|
||||
entry: 'external/egs-core/packages/utils/splat-utils/worker.ts',
|
||||
entryLabel: 'splat sort worker entry',
|
||||
fileName: 'splat-sort-worker.js',
|
||||
factorySourceFilter: /utils[\\/]splat-utils[\\/]sort\.ts$/,
|
||||
factoryName: 'WorkerFactor',
|
||||
blobUrlName: 'SplatSortWorkerBlobUrl',
|
||||
nextStatement: 'const poll =',
|
||||
}),
|
||||
];
|
||||
|
||||
const declareOnlyClasses = [
|
||||
{ name: 'Viewer', exported: 'export declare class Viewer', unexported: 'declare class Viewer' },
|
||||
{ name: 'Viewport', exported: 'export declare class Viewport', unexported: 'declare class Viewport' },
|
||||
{
|
||||
name: 'Material',
|
||||
exported: 'export declare abstract class Material',
|
||||
unexported: 'declare abstract class Material',
|
||||
},
|
||||
{ name: 'Light', exported: 'export declare abstract class Light', unexported: 'declare abstract class Light' },
|
||||
];
|
||||
|
||||
await mkdir(runtimeDir, { recursive: true });
|
||||
await mkdir(declarationDir, { recursive: true });
|
||||
await rm(tempTypesDir, { recursive: true, force: true });
|
||||
await mkdir(tempTypesDir, { recursive: true });
|
||||
|
||||
await bundleRuntime();
|
||||
emitPackageDeclarations();
|
||||
await bundleDeclarations();
|
||||
await stripPrivateTypingsImports();
|
||||
await rm(tempTypesDir, { recursive: true, force: true });
|
||||
|
||||
console.log(`[package-build] Built ${packageName}.`);
|
||||
|
||||
async function importPackageDependency(name) {
|
||||
try {
|
||||
const resolvedPath = packageRequire.resolve(name);
|
||||
|
||||
return await import(pathToFileURL(resolvedPath).href);
|
||||
} catch (error) {
|
||||
throw new Error(`Unable to load ${name} from ${packageName}. Is it listed in that package's dependencies?`, {
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function bundleRuntime() {
|
||||
const shared = {
|
||||
absWorkingDir: packageRoot,
|
||||
bundle: true,
|
||||
format: 'esm',
|
||||
logLevel: 'info',
|
||||
minify: false,
|
||||
platform: 'browser',
|
||||
sourcemap: true,
|
||||
target: 'es2020',
|
||||
treeShaking: true,
|
||||
loader: {
|
||||
'.jpg': 'dataurl',
|
||||
'.png': 'dataurl',
|
||||
},
|
||||
plugins: [workerBundlePatchPlugin(workerBundles), textureLoaderPatchPlugin(), dracoLoaderPatchPlugin()],
|
||||
};
|
||||
|
||||
await build({
|
||||
...shared,
|
||||
entryPoints: srcEntries,
|
||||
outdir: runtimeDir,
|
||||
});
|
||||
|
||||
for (const workerBundle of workerBundles) {
|
||||
await build({
|
||||
...shared,
|
||||
entryPoints: [workerBundle.entry],
|
||||
outfile: resolve(runtimeDir, workerBundle.fileName),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function emitPackageDeclarations() {
|
||||
runCommandOrExit(
|
||||
process.execPath,
|
||||
[
|
||||
packageRequire.resolve('typescript/bin/tsc'),
|
||||
'--emitDeclarationOnly',
|
||||
'--declarationMap',
|
||||
'false',
|
||||
'--outDir',
|
||||
toPosixPath(tempTypesDir),
|
||||
],
|
||||
{
|
||||
cwd: packageRoot,
|
||||
label: 'tsc --emitDeclarationOnly',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async function bundleDeclarations() {
|
||||
rollup(packageRoot, {
|
||||
typeOnlyExports: [
|
||||
{ name: 'Viewer', type: 'class', abstract: false },
|
||||
{ name: 'Viewport', type: 'class', abstract: false },
|
||||
{ name: 'Material', type: 'class', abstract: true },
|
||||
{ name: 'Light', type: 'class', abstract: true },
|
||||
],
|
||||
extractorConfig: {
|
||||
bundledPackages: [
|
||||
'@qunhe/egs',
|
||||
'@qunhe/egs-animation',
|
||||
'@qunhe/egs-texture-loader',
|
||||
'@qunhe/egs-gltf-loader',
|
||||
'@qunhe/egs-draco-loader',
|
||||
'@qunhe/egs-splat-loader',
|
||||
'@qunhe/egs-splat-utils',
|
||||
'@qunhe/egs-lib',
|
||||
],
|
||||
},
|
||||
});
|
||||
await cp(resolve(packageRoot, 'build/index.d.ts'), declarationOutput, { force: true });
|
||||
}
|
||||
|
||||
async function stripPrivateTypingsImports() {
|
||||
const source = await readFile(declarationOutput, 'utf8');
|
||||
const rewritten = source.replace(/^\s*import\s+type\s+\{\s*\}\s+from\s+["']@qunhe\/egs-typings["'];?\r?\n?/gm, '');
|
||||
|
||||
if (rewritten !== source) {
|
||||
await writeFile(declarationOutput, rewritten);
|
||||
}
|
||||
}
|
||||
|
||||
function getPackageJsonPaths(dir) {
|
||||
return listFiles(dir, { skipDirectories: ['build', 'dist', 'node_modules'] }).filter(
|
||||
filePath => basename(filePath) === 'package.json',
|
||||
);
|
||||
}
|
||||
|
||||
function isInsidePackage(filePath) {
|
||||
const relativePath = relative(packageRoot, filePath);
|
||||
|
||||
return relativePath === '' || (!!relativePath && !relativePath.startsWith('..') && !isAbsolute(relativePath));
|
||||
}
|
||||
|
||||
function getInheritedConfigPaths(configPaths, fallbackPaths) {
|
||||
const paths = Array.isArray(configPaths) && configPaths.length > 0 ? configPaths : fallbackPaths;
|
||||
|
||||
return paths.map(configPath => toRelativeConfigPath(relative(tempTypesDir, resolve(packageRoot, configPath))));
|
||||
}
|
||||
|
||||
function toRelativeConfigPath(filePath) {
|
||||
const posixPath = toPosixPath(filePath) || '.';
|
||||
|
||||
return posixPath.startsWith('.') ? posixPath : `./${posixPath}`;
|
||||
}
|
||||
|
||||
function replaceExtension(filePath, replacementExtension) {
|
||||
const currentExtension = extname(filePath);
|
||||
|
||||
return currentExtension
|
||||
? `${filePath.slice(0, -currentExtension.length)}${replacementExtension}`
|
||||
: `${filePath}${replacementExtension}`;
|
||||
}
|
||||
|
||||
function sanitizePathSegment(value) {
|
||||
return value
|
||||
.replace(/^@/, '')
|
||||
.replace(/[\\/]/g, '-')
|
||||
.replace(/[^a-zA-Z0-9._-]/g, '-');
|
||||
}
|
||||
|
||||
function splitNamedSpecifiers(specifierText) {
|
||||
return specifierText
|
||||
.split(',')
|
||||
.map(specifier => specifier.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function getNamedSpecifierName(specifier) {
|
||||
return removeTypeModifier(specifier)
|
||||
.split(/\s+as\s+/u)[0]
|
||||
.trim();
|
||||
}
|
||||
|
||||
function removeTypeModifier(specifier) {
|
||||
return specifier.replace(/^type\s+/u, '').trim();
|
||||
}
|
||||
|
||||
function createWorkerBundle(config) {
|
||||
return {
|
||||
...config,
|
||||
entry: resolveWorkspacePath(config.entry, config.entryLabel),
|
||||
};
|
||||
}
|
||||
|
||||
function workerBundlePatchPlugin(workerBundles) {
|
||||
return {
|
||||
name: 'aholo-worker-bundle-patch',
|
||||
setup(buildContext) {
|
||||
for (const workerBundle of workerBundles) {
|
||||
buildContext.onLoad({ filter: workerBundle.factorySourceFilter }, async args => {
|
||||
const source = await readFile(args.path, 'utf8');
|
||||
const contents = replaceWorkerFactory(source, workerBundle);
|
||||
|
||||
if (contents === source) {
|
||||
throw new Error(`Unable to patch ${workerBundle.entryLabel}.`);
|
||||
}
|
||||
|
||||
return {
|
||||
contents,
|
||||
loader: 'ts',
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function replaceWorkerFactory(source, workerBundle) {
|
||||
const factoryPattern = createWorkerFactoryPattern(workerBundle.factoryName, workerBundle.nextStatement);
|
||||
|
||||
return source.replace(factoryPattern, createWorkerFactoryReplacement(workerBundle));
|
||||
}
|
||||
|
||||
function createWorkerFactoryPattern(factoryName, nextStatement) {
|
||||
return new RegExp(
|
||||
`let ${escapeRegExp(factoryName)}: \\(\\) => Worker;\\s*try \\{[\\s\\S]*?\\};?\\s*${escapeRegExp(nextStatement)}`,
|
||||
);
|
||||
}
|
||||
|
||||
function createWorkerFactoryReplacement({ factoryName, blobUrlName, fileName, nextStatement }) {
|
||||
return `let ${factoryName}: () => Worker;
|
||||
let ${blobUrlName}: string | undefined;
|
||||
${factoryName} = () => {
|
||||
const workerUrl = new URL("./${fileName}", import.meta.url).href;
|
||||
|
||||
if (!${blobUrlName}) {
|
||||
const source = \`import \${JSON.stringify(workerUrl)};\`;
|
||||
${blobUrlName} = URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
|
||||
}
|
||||
|
||||
return new Worker(${blobUrlName}, { type: "module" });
|
||||
};
|
||||
${nextStatement}`;
|
||||
}
|
||||
|
||||
function textureLoaderPatchPlugin() {
|
||||
return {
|
||||
name: 'aholo-texture-loader-patch',
|
||||
setup(buildContext) {
|
||||
buildContext.onLoad(
|
||||
{ filter: /loaders[\\/]texture-loader[\\/]ktx2[\\/]basis[\\/]wasm[\\/]basis_transcoder\.js$/ },
|
||||
async args => {
|
||||
const source = await readFile(args.path, 'utf8');
|
||||
const nodeBranchShim =
|
||||
'var fs={readFileSync:function(){throw new Error("Node file loading is not available in the browser build.");}};';
|
||||
const contents = source.replace(/var fs=require\("fs"\);/, nodeBranchShim);
|
||||
|
||||
if (contents === source) {
|
||||
throw new Error('Unable to patch basis_transcoder.js node file-system branch.');
|
||||
}
|
||||
|
||||
return {
|
||||
contents,
|
||||
loader: 'js',
|
||||
};
|
||||
},
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function dracoLoaderPatchPlugin() {
|
||||
return {
|
||||
name: 'aholo-draco-loader-patch',
|
||||
setup(buildContext) {
|
||||
buildContext.onResolve({ filter: /draco_decoder\.wasm$/ }, args => ({
|
||||
path: resolve(dirname(args.importer), 'draco_decoder.wasm.js'),
|
||||
}));
|
||||
|
||||
buildContext.onLoad({ filter: /draco_decoder_wrapper\.js$/ }, async args => {
|
||||
const source = await readFile(args.path, 'utf8');
|
||||
const nodeBranchShim = `var Za={readFileSync:function(){throw new Error("Node file loading is not available in the browser build.");},readFile:function(e,b,c){c(new Error("Node file loading is not available in the browser build."));}},qa={dirname:function(){return""},normalize:function(e){return e}};`;
|
||||
const contents = source.replace(/var Za=require\("fs"\),qa=require\("path"\);/, nodeBranchShim);
|
||||
|
||||
if (contents === source) {
|
||||
throw new Error('Unable to patch draco_decoder_wrapper.js node file-system branch.');
|
||||
}
|
||||
|
||||
return {
|
||||
contents,
|
||||
loader: 'js',
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function escapeRegExp(value) {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
Reference in New Issue
Block a user