build.js: determine srcdir via import.meta.dirname

Between `build.js` and `file.js` we determine the location of `pkg` (and
various tools that we need to invoke) in one of two ways, neither of
them particularly good:

 - they're assumed to be relative to '.', which only works if ./build.js
   is invoked from the automake srcdir.  That happens to be the case
   when pkg/Makefile.am does the invocation (it does a 'cd' first) but
   it breaks if you try to run build.js manually from a different
   directory.

 - redundantly, if `$SRCDIR` is set, this will be used in some (but not
   all) places

Let's standardize on looking at `import.meta.dirname`.  This is how node
resolves imports, so it's pretty much guaranteed to be correct (or else
the script itself wouldn't be running in the first place).  This is
an absolute pathname derived from argv[1] as passed to node (ie:
build.js itself).

For the qunit plugin, `import.meta.dirname` is the path of the plugin
itself, which makes it perfect for finding the `.template.html.in` file
located immediately adjacent to itself, saving some path gymnastics.

During our computation to create `runtime-npm-modules.txt` we need to be
careful: pathnames are based on `import.meta.dirname` now and are
therefore always absolute: we need to adjust our logic for finding paths
starting with `node_modules` to consider the relative pathname.
This commit is contained in:
Allison Karlitskaya 2026-11-02 12:48:21 +01:00 committed by Allison Karlitskaya
parent af3ec756f5
commit 027ec4d836
3 changed files with 15 additions and 18 deletions

View file

@ -9,14 +9,15 @@ import process from 'node:process';
import { getFiles, getTestFiles, all_subdirs } from './files.js';
const srcdir = import.meta.dirname;
const production = process.env.NODE_ENV === 'production';
const useWasm = os.arch() != 'x64';
// ensure node_modules is present and up to date
child_process.spawnSync('tools/node-modules', ['make_package_lock_json'], { stdio: 'inherit' });
child_process.spawnSync('tools/node-modules', ['make_package_lock_json'], { stdio: 'inherit', cwd: srcdir });
// List of directories to use when resolving import statements
const nodePaths = ['pkg/lib'];
const nodePaths = [`${srcdir}/pkg/lib`];
// context options for distributed pages in dist/
const pkgOptions = {
@ -32,7 +33,7 @@ const pkgOptions = {
metafile: true,
minify: production,
nodePaths,
outbase: './pkg',
outbase: `${srcdir}/pkg`,
outdir: "./dist",
target: ['es2021'],
};
@ -43,7 +44,7 @@ const qunitOptions = {
bundle: true,
minify: false,
nodePaths,
outbase: './pkg',
outbase: `${srcdir}/pkg`,
outdir: "./qunit",
loader: {
".sh": "text",
@ -146,7 +147,7 @@ async function build() {
const { entryPoints, assetFiles, redhat_fonts } = getFiles(args.onlydir);
const tests = getTestFiles();
const testEntryPoints = tests.map(test => "pkg/" + test);
const testEntryPoints = tests.map(test => `${srcdir}/pkg/${test}`);
const pkgFirstPlugins = [
cleanPlugin({ subdir: args.onlydir }),
@ -155,7 +156,7 @@ async function build() {
const pkgPlugins = [
cockpitJSResolvePlugin,
sassPlugin({
loadPaths: [...nodePaths, 'node_modules'],
loadPaths: [...nodePaths, `${srcdir}/node_modules`],
filter: /\.scss/,
quietDeps: true,
silenceDeprecations: [
@ -267,14 +268,16 @@ async function build() {
// Extract bundled npm packages for dependency tracking
const bundledPackages = new Set();
for (const inputPath of Object.keys(results[0].metafile.inputs)) {
const rel = path.relative(srcdir, inputPath);
// Match paths like node_modules/package-name/ or node_modules/@scope/package-name/
const match = inputPath.match(/^node_modules\/(@[^/]+\/[^/]+|[^/]+)\//);
const match = rel.match(/^node_modules\/(@[^/]+\/[^/]+|[^/]+)\//);
if (match)
bundledPackages.add(match[1]);
}
// Look up versions from package-lock.json and output simple format
const packageLock = JSON.parse(fs.readFileSync('package-lock.json', 'utf8'));
const packageLock = JSON.parse(fs.readFileSync(`${srcdir}/package-lock.json`, 'utf8'));
const deps = [];
for (const pkgName of Array.from(bundledPackages).sort()) {
const lockKey = `node_modules/${pkgName}`;
@ -304,7 +307,7 @@ async function build() {
} catch (e) {} // ignore in watch mode
};
watch_dirs('pkg', on_change);
watch_dirs(`${srcdir}/pkg`, on_change);
// wait forever until Control-C
await new Promise(() => {});
}

View file

@ -1,6 +1,5 @@
import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
const info = {
entries: [
@ -154,8 +153,8 @@ const info = {
]
};
const srcdir = process.env.SRCDIR || '.';
const nodedir = path.relative(process.cwd(), path.resolve(srcdir, "node_modules"));
const srcdir = import.meta.dirname;
const nodedir = path.join(srcdir, "node_modules");
export const all_subdirs = Array.from(new Set(info.entries.map(key => key.split('/')[0])));

View file

@ -1,16 +1,11 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
const srcdir = process.env.SRCDIR || '.';
const libdir = path.resolve(srcdir, "pkg", "lib");
export const cockpitTestHtmlPlugin = ({ testFiles }) => ({
name: 'CockpitTestHtmlPlugin',
setup(build) {
build.onEnd(async () => {
const data = fs.readFileSync(path.resolve(libdir, "qunit-template.html.in"), "utf8");
const data = fs.readFileSync(path.resolve(import.meta.dirname, "qunit-template.html.in"), "utf8");
testFiles.forEach(file => {
const test = path.parse(file).name;
const builddir = file.split("/").map(() => "../").join("");