diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e3014f8..f3a72c6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,8 +10,8 @@ on: workflow_dispatch: jobs: - build: - name: Build + test: + name: Test runs-on: macos-latest env: GITHUB_ACTIONS_OUTPUT: "" @@ -49,3 +49,8 @@ jobs: run: | echo "Running in $BROWSER" npm run test:${{ matrix.browser }} + + - name: Run Build + run: | + echo "Running in $BROWSER" + npm run test:build diff --git a/package.json b/package.json index db7aca4..3cdbae5 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "test:shell": "npm run test:v8 && npm run test:jsc && npm run test:spidermonkey", "test:v8": "node tests/run-shell.mjs --shell v8", "test:jsc": "node tests/run-shell.mjs --shell jsc", - "test:spidermonkey": "node tests/run-shell.mjs --shell spidermonkey" + "test:spidermonkey": "node tests/run-shell.mjs --shell spidermonkey", + "test:build": "node tests/run-build.mjs" }, "devDependencies": { "@actions/core": "^1.11.1", diff --git a/tests/helper.mjs b/tests/helper.mjs index 0867e8a..1dc5be2 100644 --- a/tests/helper.mjs +++ b/tests/helper.mjs @@ -1,6 +1,7 @@ -import { styleText } from "node:util"; import core from "@actions/core"; +import { spawn } from "child_process"; import commandLineUsage from "command-line-usage"; +import { styleText } from "node:util"; export const GITHUB_ACTIONS_OUTPUT = "GITHUB_ACTIONS_OUTPUT" in process.env; @@ -30,6 +31,16 @@ export function logError(...args) { } } +export function logCommand(...args) { + const cmd = args.join(" "); + if (GITHUB_ACTIONS_OUTPUT) { + core.notice(styleText("blue", cmd)); + } else { + console.log(styleText("blue", cmd)); + } +} + + export async function logGroup(name, body) { if (GITHUB_ACTIONS_OUTPUT) { core.startGroup(name); @@ -80,3 +91,49 @@ export async function runTest(label, testFunction) { } return true; } + + +export async function sh(binary, ...args) { + const cmd = `${binary} ${args.join(" ")}`; + if (GITHUB_ACTIONS_OUTPUT) + core.startGroup(binary); + logCommand(cmd); + try { + return await spawnCaptureStdout(binary, args); + } catch(e) { + logError(e.stdoutString); + throw e; + } finally { + if (GITHUB_ACTIONS_OUTPUT) + core.endGroup(); + } +} + +const SPAWN_OPTIONS = Object.freeze({ + stdio: ["inherit", "pipe", "inherit"] +}); + +async function spawnCaptureStdout(binary, args, options={}) { + options = Object.assign(options, SPAWN_OPTIONS); + const childProcess = spawn(binary, args, options); + childProcess.stdout.pipe(process.stdout); + return new Promise((resolve, reject) => { + childProcess.stdoutString = ""; + childProcess.stdio[1].on("data", (data) => { + childProcess.stdoutString += data.toString(); + }); + childProcess.on("close", (code) => { + if (code === 0) { + resolve(childProcess); + } else { + // Reject the Promise with an Error on failure + const error = new Error(`Command failed with exit code ${code}: ${binary} ${args.join(" ")}`); + error.process = childProcess; + error.stdout = childProcess.stdoutString; + error.exitCode = code; + reject(error); + } + }); + childProcess.on("error", reject); + }) +} diff --git a/tests/run-build.mjs b/tests/run-build.mjs new file mode 100644 index 0000000..213b0e9 --- /dev/null +++ b/tests/run-build.mjs @@ -0,0 +1,73 @@ +#! /usr/bin/env node + +import commandLineArgs from "command-line-args"; +import fs from "fs"; +import { fileURLToPath } from "url"; +import path from "path"; + +import { logError, logCommand, printHelp, runTest, sh } from "./helper.mjs"; + +const optionDefinitions = [ + { name: "help", alias: "h", description: "Print this help text." }, +]; + +const options = commandLineArgs(optionDefinitions); + +if ("help" in options) + printHelp(optionDefinitions); + +const FILE_PATH = fileURLToPath(import.meta.url); +const SRC_DIR = path.dirname(path.dirname(FILE_PATH)); + +async function findPackageJsonFiles(dir, accumulator=[]) { + const dirEntries = fs.readdirSync(dir, { withFileTypes: true }); + for (const dirent of dirEntries) { + if (dirent.name === "node_modules" || dirent.name === ".git") + continue; + const fullPath = path.join(dir, dirent.name); + if (dirent.isDirectory()) + findPackageJsonFiles(fullPath, accumulator); + else if (dirent.name === "package.json") + accumulator.push(fullPath) + } + return accumulator; +} + +async function runBuilds() { + const packageJsonFiles = await findPackageJsonFiles(SRC_DIR); + let success = true; + + for (const file of packageJsonFiles) { + const content = fs.readFileSync(file, "utf-8"); + const packageJson = JSON.parse(content); + if (!packageJson.scripts?.build) { + continue; + } + + const dir = path.dirname(file); + const relativeDir = path.relative(SRC_DIR, dir); + const testName = `Building ${relativeDir}`; + + const buildTask = async () => { + const oldCWD = process.cwd(); + try { + logCommand("cd", dir); + process.chdir(dir); + await sh("npm", "ci"); + await sh("npm", "run", "build"); + } finally { + process.chdir(oldCWD); + // await sh("git", "reset", "--hard"); + } + }; + + success &&= await runTest(testName, buildTask); + } + + if (!success) { + logError("One or more builds failed."); + process.exit(1); + } +} + +setImmediate(runBuilds); \ No newline at end of file diff --git a/tests/run-shell.mjs b/tests/run-shell.mjs index 1b626a7..b4f04c3 100644 --- a/tests/run-shell.mjs +++ b/tests/run-shell.mjs @@ -1,15 +1,12 @@ #! /usr/bin/env node import commandLineArgs from "command-line-args"; -import { spawn } from "child_process"; -import { fileURLToPath } from "url"; -import { styleText } from "node:util"; -import * as path from "path"; import * as fs from "fs"; import * as os from "os"; -import core from "@actions/core"; +import * as path from "path"; +import { fileURLToPath } from "url"; -import {logInfo, logError, logGroup, printHelp, runTest, GITHUB_ACTIONS_OUTPUT} from "./helper.mjs"; +import { logGroup, logInfo, printHelp, runTest, sh } from "./helper.mjs"; const optionDefinitions = [ { name: "shell", type: String, description: "Set the shell to test, choices are [jsc, v8, spidermonkey]." }, @@ -55,55 +52,6 @@ function convertCliArgs(cli, ...cliArgs) { } -const SPAWN_OPTIONS = { - stdio: ["inherit", "inherit", "inherit"] -}; - -async function sh(binary, ...args) { - const cmd = `${binary} ${args.join(" ")}`; - if (GITHUB_ACTIONS_OUTPUT) { - core.startGroup(binary); - core.notice(styleText("blue", cmd)); - } else { - console.log(styleText("blue", cmd)); - } - try { - const result = await spawnCaptureStdout(binary, args, SPAWN_OPTIONS); - if (result.status || result.error) { - logError(result.error); - throw new Error(`Shell CMD failed: ${binary} ${args.join(" ")}`); - } - return result; - } finally { - if (GITHUB_ACTIONS_OUTPUT) - core.endGroup(); - } -} - -async function spawnCaptureStdout(binary, args) { - const childProcess = spawn(binary, args); - childProcess.stdout.pipe(process.stdout); - return new Promise((resolve, reject) => { - childProcess.stdoutString = ""; - childProcess.stdio[1].on("data", (data) => { - childProcess.stdoutString += data.toString(); - }); - childProcess.on('close', (code) => { - if (code === 0) { - resolve(childProcess); - } else { - // Reject the Promise with an Error on failure - const error = new Error(`Command failed with exit code ${code}: ${binary} ${args.join(" ")}`); - error.process = childProcess; - error.stdout = childProcess.stdoutString; - error.exitCode = code; - reject(error); - } - }); - childProcess.on('error', reject); - }) -} - async function runTests() { const shellBinary = await logGroup(`Installing JavaScript Shell: ${SHELL_NAME}`, testSetup); let success = true; diff --git a/web-ssr/package-lock.json b/web-ssr/package-lock.json index cba3e15..561c631 100644 --- a/web-ssr/package-lock.json +++ b/web-ssr/package-lock.json @@ -14,6 +14,7 @@ "@babel/node": "^7.28.0", "@babel/preset-env": "^7.28.0", "@babel/preset-react": "^7.27.1", + "@dapplets/unicode-escape-webpack-plugin": "^0.1.1", "assert": "^2.1.0", "babel-loader": "^10.0.0", "babel-node": "^0.0.1-security", @@ -1641,6 +1642,12 @@ "node": ">=6.9.0" } }, + "node_modules/@dapplets/unicode-escape-webpack-plugin": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@dapplets/unicode-escape-webpack-plugin/-/unicode-escape-webpack-plugin-0.1.1.tgz", + "integrity": "sha512-dBcrCWE6ZOOHD1XLe3raXk29CwQSJQCXYx8QNcCMvfG+cZ9tZh1h2OVV7D936pmues8OTm1KhjWeiZXbH30SAg==", + "license": "MIT" + }, "node_modules/@discoveryjs/json-ext": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", diff --git a/web-ssr/package.json b/web-ssr/package.json index 973d193..1f58b47 100644 --- a/web-ssr/package.json +++ b/web-ssr/package.json @@ -18,6 +18,7 @@ "@babel/node": "^7.28.0", "@babel/preset-env": "^7.28.0", "@babel/preset-react": "^7.27.1", + "@dapplets/unicode-escape-webpack-plugin": "^0.1.1", "assert": "^2.1.0", "babel-loader": "^10.0.0", "babel-node": "^0.0.1-security",