diff --git a/src/schematics/deploy/actions.ts b/src/schematics/deploy/actions.ts index 9a2fb7cde..cecf255bf 100644 --- a/src/schematics/deploy/actions.ts +++ b/src/schematics/deploy/actions.ts @@ -13,8 +13,9 @@ import * as winston from 'winston'; import { BuildTarget, CloudRunOptions, DeployBuilderSchema, FSHost, FirebaseTools } from '../interfaces'; import { DEFAULT_FUNCTION_NAME, defaultFunction, defaultPackage, dockerfile, functionGen2 } from './functions-templates.js'; -// @ts-ignore -const __dirname = dirname(fileURLToPath(import.meta.url)); +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore `import.meta` is rejected by the --module es2015 pass of `npm run build:jasmine`. +const moduleDirectory = typeof __dirname === 'string' ? __dirname : dirname(fileURLToPath(import.meta.url)); const { copySync, removeSync, readJsonSync } = fsExtra; @@ -128,7 +129,7 @@ const findPackageVersion = (packageManager: string, name: string) => { const getPackageJson = (context: BuilderContext, workspaceRoot: string, options: DeployBuilderOptions, main?: string) => { const dependencies: Record = {}; const devDependencies: Record = {}; - const { firebaseFunctionsDependencies } = readJsonSync(join(__dirname, '..', 'versions.json')); + const { firebaseFunctionsDependencies } = readJsonSync(join(moduleDirectory, '..', 'versions.json')); if (options.ssr !== 'cloud-run') { Object.keys(firebaseFunctionsDependencies).forEach(name => { const { version, dev } = firebaseFunctionsDependencies[name]; diff --git a/tools/build.ts b/tools/build.ts index 0d8dd38c4..ccdd02e63 100644 --- a/tools/build.ts +++ b/tools/build.ts @@ -313,17 +313,21 @@ function spawnPromise(command: string, args: string[]) { .on('error', reject)); } +// Path segments of each schematic entry point, relative to `schematics/` and without the file +// extension: esbuild compiles the `.ts` and loadCompiledSchematics requires the emitted `.js`. +const schematicEntryPoints = [ + ['update', 'index'], + ['deploy', 'actions'], + ['deploy', 'builder'], + ['add', 'index'], + ['setup', 'index'], + ['update', 'v7', 'index'], + ['update', 'v21', 'index'], +]; + async function compileSchematics() { await esbuild.build({ - entryPoints: [ - src('schematics', "update", "index.ts"), - src('schematics', "deploy", "actions.ts"), - src('schematics', "deploy", "builder.ts"), - src('schematics', "add", "index.ts"), - src('schematics', "setup", "index.ts"), - src('schematics', "update", "v7", "index.ts"), - src('schematics', "update", "v21", "index.ts"), - ], + entryPoints: schematicEntryPoints.map(segments => `${src('schematics', ...segments)}.ts`), format: "cjs", // turns out schematics don't support ESM, need to use webpack or shim these // format: "esm", @@ -357,6 +361,25 @@ async function compileSchematics() { copy(src('schematics', 'setup', 'schema.json'), dest('schematics', 'setup', 'schema.json')), ]); await replaceSchematicVersions(); + await loadCompiledSchematics(); +} + +/** + * Loads every compiled schematic entry point, so a bundle that cannot even be required fails the + * build instead of shipping. + */ +async function loadCompiledSchematics() { + const failures: string[] = []; + for (const segments of schematicEntryPoints) { + try { + require(`${dest('schematics', ...segments)}.js`); + } catch (error) { + failures.push(` ${join(...segments)}.js: ${error}`); + } + } + if (failures.length) { + throw new Error(`Compiled schematics failed to load:\n${failures.join('\n')}`); + } } async function buildLibrary() {