From d82316eba4bf07c1653f7203420569242bc75ec9 Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Thu, 30 Jan 2025 16:34:58 -0500 Subject: [PATCH 01/12] watch 2 https://coveord.atlassian.net/browse/KIT-3920 --- packages/atomic/scripts/watch2.js | 151 ++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 packages/atomic/scripts/watch2.js diff --git a/packages/atomic/scripts/watch2.js b/packages/atomic/scripts/watch2.js new file mode 100644 index 0000000000..fc21e0e7db --- /dev/null +++ b/packages/atomic/scripts/watch2.js @@ -0,0 +1,151 @@ +import chalk from 'chalk'; +import {exec} from 'node:child_process'; +import {watch} from 'node:fs'; +import ora from 'ora'; + +let runningProcesses = []; +let isStopped = false; +let currentTask = null; + +function runCommand(command) { + return new Promise((resolve, reject) => { + if (isStopped) { + return resolve(); + } + + const process = exec(command); + runningProcesses.push(process); + + process.stdout.on('data', (data) => console.log(chalk.green(data))); + process.stderr.on('data', (data) => console.error(chalk.red(data))); + + process.on('close', (code, signal) => { + runningProcesses = runningProcesses.filter((p) => p !== process); + + if (isStopped || signal === 'SIGTERM' || signal === 'SIGINT') { + console.log(chalk.yellow(`⚠️ Process for "${command}" was stopped.`)); + return resolve(); + } + + if (code === 0) { + resolve(); + } else { + reject(new Error(`Command failed with exit code ${code}`)); + } + }); + + process.on('error', (err) => { + if (isStopped) { + return resolve(); + } + reject(err); + }); + }); +} + +function funkyAnimation(text) { + if (isStopped) { + return Promise.resolve(); + } + + const frames = ['🍎', '🚀', '🔥', '💥', '✨', '🌟', '🎉', '🍏']; + const spinner = ora({ + text: text, + spinner: {frames, interval: 200}, + }).start(); + + return new Promise((resolve) => { + setTimeout(() => { + if (!isStopped) { + spinner.succeed(chalk.magenta.bold(`${text} ✅`)); + } + resolve(); + }, 3000); + }); +} + +async function stopAllProcesses() { + console.log(chalk.red('🛑 Stopping all running processes...')); + isStopped = true; + + runningProcesses.forEach((process) => { + if (process && process.kill) { + process.kill('SIGTERM'); + } + }); + + runningProcesses = []; + currentTask = null; + + // Wait briefly to ensure processes terminate before restarting the build + await new Promise((resolve) => setTimeout(resolve, 100)); +} + +watch('src', {recursive: true}, async (eventType, filename) => { + await stopAllProcesses(); // Ensure all previous tasks are stopped before continuing + + console.log(chalk.cyanBright(`📂 File changed: ${filename}`)); + console.log(chalk.yellowBright(`🔄 Event Type: ${eventType}`)); + + isStopped = false; // Allow execution again + + try { + currentTask = funkyAnimation('🍎 Rebuilding Stencil...'); + await currentTask; + if (isStopped) { + return; + } + + currentTask = runCommand( + 'node --max_old_space_size=6144 ../../node_modules/@stencil/core/bin/stencil build --tsConfig tsconfig.stencil.json' + ); + await currentTask; + if (isStopped) { + return; + } + + currentTask = funkyAnimation('🍏 Placing the Stencil Proxy...'); + await currentTask; + if (isStopped) { + return; + } + + currentTask = runCommand('node ./scripts/stencil-proxy.mjs'); + await currentTask; + if (isStopped) { + return; + } + + currentTask = funkyAnimation('🚀 Running esbuild for autoloader ESM...'); + await currentTask; + if (isStopped) { + return; + } + + currentTask = runCommand( + 'esbuild src/autoloader/index.ts --format=esm --outfile=dist/atomic/autoloader/index.esm.js' + ); + await currentTask; + if (isStopped) { + return; + } + + currentTask = funkyAnimation('🔥 Running esbuild for autoloader CJS...'); + await currentTask; + if (isStopped) { + return; + } + + currentTask = runCommand( + 'esbuild src/autoloader/index.ts --format=cjs --outfile=dist/atomic/autoloader/index.cjs.js' + ); + await currentTask; + if (isStopped) { + return; + } + + console.log(chalk.bold.bgMagenta(' 🎇 Build process completed! 🎇 ')); + } catch (error) { + console.log(chalk.red(`❌ Build process failed: ${error.message}`)); + } +}); From 50e0385e450e1c6e40e10a907c035cc28c453cab Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Fri, 31 Jan 2025 10:30:20 -0500 Subject: [PATCH 02/12] watch3 script https://coveord.atlassian.net/browse/KIT-3920 --- packages/atomic/project.json | 9 +- packages/atomic/scripts/watch.mjs | 5 +- packages/atomic/scripts/watch2.js | 26 ++- packages/atomic/scripts/watch3.js | 191 ++++++++++++++++++ .../generated-answer-common.tsx | 1 + .../facets/atomic-facet/atomic-facet.tsx | 2 + 6 files changed, 220 insertions(+), 14 deletions(-) create mode 100644 packages/atomic/scripts/watch3.js diff --git a/packages/atomic/project.json b/packages/atomic/project.json index 7abd174c04..dd5ce56a40 100644 --- a/packages/atomic/project.json +++ b/packages/atomic/project.json @@ -93,7 +93,7 @@ } }, "dev": { - "dependsOn": ["storybook", "build:watch", "cem:dev", "web:dev"], + "dependsOn": ["storybook", "watch2", "cem:dev", "web:dev"], "executor": "nx:noop" }, "build:watch": { @@ -176,6 +176,13 @@ "command": "rm -f ./dist/atomic/loader/package.json", "cwd": "packages/atomic" } + }, + "watch3": { + "executor": "nx:run-commands", + "options": { + "command": "node ./scripts/watch3.js", + "cwd": "{projectRoot}" + } } } } diff --git a/packages/atomic/scripts/watch.mjs b/packages/atomic/scripts/watch.mjs index 5542253045..2e952122d8 100644 --- a/packages/atomic/scripts/watch.mjs +++ b/packages/atomic/scripts/watch.mjs @@ -20,10 +20,7 @@ function checkPort(port) { }); } -async function rebuild(event, fileName) { - if (fileName.contains('/e2e/') || fileName.contains('/.e2e.')) { - return; - } +async function rebuild() { const commands = [ 'node --max_old_space_size=6144 ../../node_modules/@stencil/core/bin/stencil build --tsConfig tsconfig.stencil.json', 'node ./scripts/stencil-proxy.mjs', diff --git a/packages/atomic/scripts/watch2.js b/packages/atomic/scripts/watch2.js index fc21e0e7db..fb150f451b 100644 --- a/packages/atomic/scripts/watch2.js +++ b/packages/atomic/scripts/watch2.js @@ -1,12 +1,15 @@ import chalk from 'chalk'; import {exec} from 'node:child_process'; import {watch} from 'node:fs'; +// import util from 'node:util'; import ora from 'ora'; let runningProcesses = []; let isStopped = false; let currentTask = null; +// const execPromise = util.promisify(exec); + function runCommand(command) { return new Promise((resolve, reject) => { if (isStopped) { @@ -43,7 +46,7 @@ function runCommand(command) { }); } -function funkyAnimation(text) { +async function funkyAnimation(text, action) { if (isStopped) { return Promise.resolve(); } @@ -54,14 +57,19 @@ function funkyAnimation(text) { spinner: {frames, interval: 200}, }).start(); - return new Promise((resolve) => { - setTimeout(() => { - if (!isStopped) { - spinner.succeed(chalk.magenta.bold(`${text} ✅`)); - } - resolve(); - }, 3000); - }); + try { + await action(); + spinner.succeed(chalk.magenta.bold(`${text} ✅`)); + } catch (error) { + spinner.fail(chalk.red.bold(`${text} ❌`)); + if (error.stdout) { + console.error(chalk.red(error.stdout)); + } + if (error.stderr) { + console.error(chalk.red(error.stderr)); + } + process.exit(1); + } } async function stopAllProcesses() { diff --git a/packages/atomic/scripts/watch3.js b/packages/atomic/scripts/watch3.js new file mode 100644 index 0000000000..b4fbdc9b2e --- /dev/null +++ b/packages/atomic/scripts/watch3.js @@ -0,0 +1,191 @@ +import chalk from 'chalk'; +import {exec} from 'node:child_process'; +import {watch} from 'node:fs'; +import net from 'node:net'; +import ora from 'ora'; + +let runningProcesses = []; +let isStopped = false; +let storybookServer; + +async function nextTask(text, command) { + const frames = [ + '⚽ 🐕 ', // Ball at the start, dog behind + ' ⚽ 🐕 ', // Dog moves closer + ' ⚽🐕 ', // Almost touching the ball + ' ⚽🐕 ', // Dog pushes the ball + '⚽ 🐕 ', // Ball moves forward + ]; + const spinner = ora({ + text: text, + spinner: {frames, interval: 200}, + }).start(); + + return new Promise((resolve, reject) => { + const childProcess = exec(command, {stdio: 'inherit'}); + + runningProcesses.push(childProcess); + + childProcess.on('exit', (code) => { + runningProcesses = runningProcesses.filter((p) => p !== childProcess); + + if (code === 0) { + spinner.succeed(chalk.magenta.bold(`${text}`)); + resolve(); + } else { + spinner.fail(chalk.red.bold(`${text}`)); + } + }); + + childProcess.on('error', (error) => { + spinner.fail(chalk.red.bold(`${text}`)); + reject(error); + }); + }); +} + +async function stopAllProcesses() { + isStopped = true; + + runningProcesses.forEach((process) => { + if (process && process.kill) { + process.kill('SIGTERM'); + } + }); + + runningProcesses = []; + + // Wait briefly to ensure processes terminate before restarting the build + await new Promise((resolve) => setTimeout(resolve, 100)); +} + +function waitForPort(port, host = 'localhost', timeout = 30000) { + return new Promise((resolve, reject) => { + const start = Date.now(); + + function check() { + const socket = net.createConnection(port, host); + socket.setTimeout(1000); + socket.on('connect', () => { + socket.destroy(); + resolve(); + }); + socket.on('timeout', () => { + socket.destroy(); + if (Date.now() - start > timeout) { + reject(new Error(`Timeout waiting for port ${port}`)); + } else { + setTimeout(check, 500); + } + }); + socket.on('error', () => { + socket.destroy(); + if (Date.now() - start > timeout) { + reject(new Error(`Timeout waiting for port ${port}`)); + } else { + setTimeout(check, 500); + } + }); + } + + check(); + }); +} + +async function startServers() { + console.log(chalk.green.bold('🚀 Starting development servers...')); + + storybookServer = exec('npx storybook dev -p 4400', {stdio: 'ignore'}); + + exec('vite serve dev', {stdio: 'ignore'}); + + console.log( + chalk.yellow('⌛ Waiting for Storybook (4400) and Vite (3333)...') + ); + await Promise.all([waitForPort(4400), waitForPort(3333)]); + + console.log(chalk.blue.bold('✅ Servers started! Watching for changes...')); +} + +// Start the servers first +startServers(); + +watch('src', {recursive: true}, async (eventType, filename) => { + await stopAllProcesses(); + console.log(chalk.cyanBright(`📂 File changed: ${filename}`)); + + isStopped = false; + + await nextTask( + 'Rebuilding Stencil...', + 'node --max_old_space_size=6144 ../../node_modules/@stencil/core/bin/stencil build --tsConfig tsconfig.stencil.json' + ); + + if (isStopped) { + return; + } + + await nextTask( + 'Placing the Stencil Proxy...', + 'node ./scripts/stencil-proxy.mjs' + ); + + if (isStopped) { + return; + } + + await nextTask( + 'Rebuilding Lit...', + 'node ./scripts/build.mjs --config=tsconfig.lit.json' + ); + + if (isStopped) { + return; + } + + await nextTask( + 'Process CSS...', + 'node ./scripts/process-css.mjs --config=tsconfig.lit.json' + ); + + if (isStopped) { + return; + } + + await nextTask( + 'Running esbuild for autoloader ESM...', + 'esbuild src/autoloader/index.ts --format=esm --outfile=dist/atomic/autoloader/index.esm.js' + ); + + if (isStopped) { + return; + } + + await nextTask( + 'Running esbuild for autoloader CJS...', + 'esbuild src/autoloader/index.ts --format=cjs --outfile=dist/atomic/autoloader/index.cjs.js' + ); + + if (isStopped) { + return; + } + + await nextTask('CEM build...', 'cem analyze'); + + if (isStopped) { + return; + } + + await nextTask('Building storybook', 'npx storybook build -o dist-storybook'); + + if (isStopped) { + return; + } + + // restart storybook server, somehow even after build, it doesn't pick up the changes + // it needs a dev restart to pick up the changes + storybookServer.kill('SIGTERM'); + exec('npx storybook dev -p 4400', {stdio: 'ignore'}); + + console.log(chalk.magenta.bold(' 🎇 Build process completed! 🎇 ')); +}); diff --git a/packages/atomic/src/components/common/generated-answer/generated-answer-common.tsx b/packages/atomic/src/components/common/generated-answer/generated-answer-common.tsx index b2601fd0e7..751474e7e8 100644 --- a/packages/atomic/src/components/common/generated-answer/generated-answer-common.tsx +++ b/packages/atomic/src/components/common/generated-answer/generated-answer-common.tsx @@ -419,6 +419,7 @@ export class GeneratedAnswerCommon { } public render() { + console.log('I did changes'); if (this.shouldBeHidden) { return null; } diff --git a/packages/atomic/src/components/search/facets/atomic-facet/atomic-facet.tsx b/packages/atomic/src/components/search/facets/atomic-facet/atomic-facet.tsx index 6cd3b45a06..48a79140b2 100644 --- a/packages/atomic/src/components/search/facets/atomic-facet/atomic-facet.tsx +++ b/packages/atomic/src/components/search/facets/atomic-facet/atomic-facet.tsx @@ -185,6 +185,7 @@ export class AtomicFacet implements InitializableComponent { */ @Prop({reflect: true}) public resultsMustMatch: FacetResultsMustMatch = 'atLeastOneValue'; + /** * Whether to display the facet values as checkboxes (multiple selection), links (single selection) or boxes (multiple selection). * Possible values are 'checkbox', 'link', and 'box'. @@ -348,6 +349,7 @@ export class AtomicFacet implements InitializableComponent { firstSearchExecuted={this.searchStatusState.firstSearchExecuted} hasResults={this.facetState.values.length > 0} > + poop changesss {this.searchStatusState.firstSearchExecuted ? ( Date: Fri, 31 Jan 2025 10:55:26 -0500 Subject: [PATCH 03/12] new script https://coveord.atlassian.net/browse/KIT-3920 --- packages/atomic/project.json | 29 +---------- packages/atomic/scripts/{watch3.js => dev.js} | 51 ++++++++++--------- 2 files changed, 28 insertions(+), 52 deletions(-) rename packages/atomic/scripts/{watch3.js => dev.js} (80%) diff --git a/packages/atomic/project.json b/packages/atomic/project.json index d61a2d3401..1cbcd7533e 100644 --- a/packages/atomic/project.json +++ b/packages/atomic/project.json @@ -92,10 +92,6 @@ "cwd": "{projectRoot}" } }, - "dev": { - "dependsOn": ["storybook", "watch2", "cem:dev", "web:dev"], - "executor": "nx:noop" - }, "build:watch": { "executor": "nx:run-commands", "options": { @@ -104,13 +100,6 @@ "waitUntilTargets": ["^dev"] } }, - "web:dev": { - "executor": "nx:run-commands", - "options": { - "command": "node ./scripts/start-dev.mjs", - "cwd": "{projectRoot}" - } - }, "stencil:dev": { "dependsOn": ["^build", "build:locales"], "executor": "nx:run-commands", @@ -119,13 +108,6 @@ "cwd": "{projectRoot}" } }, - "cem:dev": { - "executor": "nx:run-commands", - "options": { - "command": "cem analyze --watch", - "cwd": "{projectRoot}" - } - }, "cem:build": { "executor": "nx:run-commands", "options": { @@ -147,13 +129,6 @@ "command": "wait-on http://localhost:3333/ping" } }, - "storybook": { - "executor": "nx:run-commands", - "options": { - "cwd": "{projectRoot}", - "command": "npx storybook dev -p 4400" - } - }, "build-storybook": { "dependsOn": ["cached:build:stencil", "cem:build", "list-assets"], "executor": "nx:run-commands", @@ -198,10 +173,10 @@ "required": ["name", "output"] } }, - "watch3": { + "dev": { "executor": "nx:run-commands", "options": { - "command": "node ./scripts/watch3.js", + "command": "node ./scripts/dev.js", "cwd": "{projectRoot}" } } diff --git a/packages/atomic/scripts/watch3.js b/packages/atomic/scripts/dev.js similarity index 80% rename from packages/atomic/scripts/watch3.js rename to packages/atomic/scripts/dev.js index b4fbdc9b2e..c5b5615151 100644 --- a/packages/atomic/scripts/watch3.js +++ b/packages/atomic/scripts/dev.js @@ -9,13 +9,7 @@ let isStopped = false; let storybookServer; async function nextTask(text, command) { - const frames = [ - '⚽ 🐕 ', // Ball at the start, dog behind - ' ⚽ 🐕 ', // Dog moves closer - ' ⚽🐕 ', // Almost touching the ball - ' ⚽🐕 ', // Dog pushes the ball - '⚽ 🐕 ', // Ball moves forward - ]; + const frames = ['⚽ 🐕 ', ' ⚽ 🐕 ', ' ⚽🐕 ', ' ⚽🐕 ', '⚽ 🐕 ']; const spinner = ora({ text: text, spinner: {frames, interval: 200}, @@ -95,9 +89,12 @@ function waitForPort(port, host = 'localhost', timeout = 30000) { async function startServers() { console.log(chalk.green.bold('🚀 Starting development servers...')); - storybookServer = exec('npx storybook dev -p 4400', {stdio: 'ignore'}); + storybookServer = exec('npx storybook dev -p 4400 --no-open', { + stdio: 'ignore', + }); - exec('vite serve dev', {stdio: 'ignore'}); + // Script that starts the Vite server and copies files for CDN mode + exec('node ./scripts/start-dev.mjs', {stdio: 'ignore'}); console.log( chalk.yellow('⌛ Waiting for Storybook (4400) and Vite (3333)...') @@ -110,29 +107,30 @@ async function startServers() { // Start the servers first startServers(); -watch('src', {recursive: true}, async (eventType, filename) => { +// Watch the src folder for changes +watch('src', {recursive: true}, async (_, filename) => { await stopAllProcesses(); console.log(chalk.cyanBright(`📂 File changed: ${filename}`)); isStopped = false; - await nextTask( - 'Rebuilding Stencil...', - 'node --max_old_space_size=6144 ../../node_modules/@stencil/core/bin/stencil build --tsConfig tsconfig.stencil.json' - ); + // await nextTask( + // 'Rebuilding Stencil...', + // 'node --max_old_space_size=6144 ../../node_modules/@stencil/core/bin/stencil build --tsConfig tsconfig.stencil.json' + // ); - if (isStopped) { - return; - } + // if (isStopped) { + // return; + // } - await nextTask( - 'Placing the Stencil Proxy...', - 'node ./scripts/stencil-proxy.mjs' - ); + // await nextTask( + // 'Placing the Stencil Proxy...', + // 'node ./scripts/stencil-proxy.mjs' + // ); - if (isStopped) { - return; - } + // if (isStopped) { + // return; + // } await nextTask( 'Rebuilding Lit...', @@ -185,7 +183,10 @@ watch('src', {recursive: true}, async (eventType, filename) => { // restart storybook server, somehow even after build, it doesn't pick up the changes // it needs a dev restart to pick up the changes storybookServer.kill('SIGTERM'); - exec('npx storybook dev -p 4400', {stdio: 'ignore'}); + exec('npx storybook dev -p 4400 --no-open', {stdio: 'ignore'}); console.log(chalk.magenta.bold(' 🎇 Build process completed! 🎇 ')); }); + +// test is with a lit component +// Add the flag From b0ec0db29eaef8be82863150a4de814b9bb225d5 Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Fri, 31 Jan 2025 13:43:42 -0500 Subject: [PATCH 04/12] final dev mode https://coveord.atlassian.net/browse/KIT-3920 --- packages/atomic/.storybook/main.mts | 42 ++++- packages/atomic/project.json | 8 + packages/atomic/scripts/dev.js | 53 +++--- .../component.ts.hbs | 2 +- packages/atomic/scripts/watch.mjs | 46 ----- packages/atomic/scripts/watch2.js | 159 ------------------ packages/atomic/tsconfig.json | 5 +- packages/atomic/tsconfig.lit.json | 5 +- packages/atomic/tsconfig.stencil.json | 5 +- 9 files changed, 75 insertions(+), 250 deletions(-) delete mode 100644 packages/atomic/scripts/watch.mjs delete mode 100644 packages/atomic/scripts/watch2.js diff --git a/packages/atomic/.storybook/main.mts b/packages/atomic/.storybook/main.mts index 34c623ac6e..f73fde71a7 100644 --- a/packages/atomic/.storybook/main.mts +++ b/packages/atomic/.storybook/main.mts @@ -63,15 +63,17 @@ const config: StorybookConfig = { mergeConfig(config, { plugins: [ nxViteTsPaths(), - resolveStorybookUtils(), + resolveStorybookUtilsImports(), + resolveSrcImports(), + forceInlineCssImports(), configType === 'PRODUCTION' && isCDN && externalizeDependencies(), ], }), }; -const resolveStorybookUtils: PluginImpl = () => { +const resolveStorybookUtilsImports: PluginImpl = () => { return { - name: 'resolve-storybook-utils', + name: 'resolve-storybook-utils-imports', async resolveId(source: string, importer, options) { if (source.startsWith('@/storybook-utils')) { return this.resolve( @@ -87,4 +89,38 @@ const resolveStorybookUtils: PluginImpl = () => { }; }; +const resolveSrcImports: PluginImpl = () => { + return { + name: 'resolve-storybook-utils-imports', + async resolveId(source: string, importer, options) { + if (source.startsWith('@/src')) { + return this.resolve( + source.replace('@/src', path.resolve(__dirname, '../src')), + importer, + options + ); + } + }, + }; +}; + +const forceInlineCssImports: PluginImpl = () => { + return { + name: 'force-inline-css-imports', + enforce: 'pre', + transform(code, id) { + if (id.endsWith('.ts')) { + return { + code: code.replace( + /import\s+([^'"]+)\s+from\s+['"]([^'"]+\.css)['"]/g, + (_, importName, cssPath) => + `import ${importName} from '${cssPath}?inline'` + ), + map: null, + }; + } + return null; + }, + }; +}; export default config; diff --git a/packages/atomic/project.json b/packages/atomic/project.json index 1cbcd7533e..e5b0342f3b 100644 --- a/packages/atomic/project.json +++ b/packages/atomic/project.json @@ -178,6 +178,14 @@ "options": { "command": "node ./scripts/dev.js", "cwd": "{projectRoot}" + }, + "schema": { + "properties": { + "stencil": { + "type": "boolean", + "description": "If you want to rebuild stencil in dev mode" + } + } } } } diff --git a/packages/atomic/scripts/dev.js b/packages/atomic/scripts/dev.js index c5b5615151..417d88ca4a 100644 --- a/packages/atomic/scripts/dev.js +++ b/packages/atomic/scripts/dev.js @@ -107,30 +107,35 @@ async function startServers() { // Start the servers first startServers(); +const [isStencil] = process.argv.slice(2); + // Watch the src folder for changes watch('src', {recursive: true}, async (_, filename) => { + console.log(isStencil); await stopAllProcesses(); console.log(chalk.cyanBright(`📂 File changed: ${filename}`)); isStopped = false; - // await nextTask( - // 'Rebuilding Stencil...', - // 'node --max_old_space_size=6144 ../../node_modules/@stencil/core/bin/stencil build --tsConfig tsconfig.stencil.json' - // ); + if (isStencil) { + await nextTask( + 'Rebuilding Stencil...', + 'node --max_old_space_size=6144 ../../node_modules/@stencil/core/bin/stencil build --tsConfig tsconfig.stencil.json' + ); - // if (isStopped) { - // return; - // } + if (isStopped) { + return; + } - // await nextTask( - // 'Placing the Stencil Proxy...', - // 'node ./scripts/stencil-proxy.mjs' - // ); + await nextTask( + 'Placing the Stencil Proxy...', + 'node ./scripts/stencil-proxy.mjs' + ); - // if (isStopped) { - // return; - // } + if (isStopped) { + return; + } + } await nextTask( 'Rebuilding Lit...', @@ -142,7 +147,7 @@ watch('src', {recursive: true}, async (_, filename) => { } await nextTask( - 'Process CSS...', + 'Processing CSS...', 'node ./scripts/process-css.mjs --config=tsconfig.lit.json' ); @@ -168,25 +173,15 @@ watch('src', {recursive: true}, async (_, filename) => { return; } - await nextTask('CEM build...', 'cem analyze'); - - if (isStopped) { - return; - } - await nextTask('Building storybook', 'npx storybook build -o dist-storybook'); - - if (isStopped) { - return; - } - // restart storybook server, somehow even after build, it doesn't pick up the changes // it needs a dev restart to pick up the changes storybookServer.kill('SIGTERM'); exec('npx storybook dev -p 4400 --no-open', {stdio: 'ignore'}); + if (isStopped) { + return; + } + console.log(chalk.magenta.bold(' 🎇 Build process completed! 🎇 ')); }); - -// test is with a lit component -// Add the flag diff --git a/packages/atomic/scripts/generate-component-templates/component.ts.hbs b/packages/atomic/scripts/generate-component-templates/component.ts.hbs index b3420bfc00..b3dfe8f6c4 100644 --- a/packages/atomic/scripts/generate-component-templates/component.ts.hbs +++ b/packages/atomic/scripts/generate-component-templates/component.ts.hbs @@ -1,6 +1,6 @@ -import {TailwindLitElement} from '@/src/utils/tailwind.element'; import {CSSResultGroup, html, unsafeCSS} from 'lit'; import {customElement, property} from 'lit/decorators.js'; +import {TailwindLitElement} from '../../../utils/tailwind.element'; import styles from './{{name}}.css'; /** diff --git a/packages/atomic/scripts/watch.mjs b/packages/atomic/scripts/watch.mjs deleted file mode 100644 index 2e952122d8..0000000000 --- a/packages/atomic/scripts/watch.mjs +++ /dev/null @@ -1,46 +0,0 @@ -import {execSync} from 'node:child_process'; -import {watch, cpSync} from 'node:fs'; -import {createServer} from 'node:http'; - -function checkPort(port) { - return new Promise((resolve, reject) => { - const server = createServer(); - server.once('error', (err) => { - if (err.code === 'EADDRINUSE') { - resolve(true); - } else { - reject(err); - } - }); - server.once('listening', () => { - server.close(); - resolve(false); - }); - server.listen(port); - }); -} - -async function rebuild() { - const commands = [ - 'node --max_old_space_size=6144 ../../node_modules/@stencil/core/bin/stencil build --tsConfig tsconfig.stencil.json', - 'node ./scripts/stencil-proxy.mjs', - 'node ./scripts/build.mjs --config=tsconfig.lit.json', - 'node ./scripts/process-css.mjs --config=tsconfig.lit.json ', - 'if [ "$DEPLOYMENT_ENVIRONMENT" == "CDN" ]; then rollup -c rollup.config.js; fi', - 'esbuild src/autoloader/index.ts --format=esm --outfile=dist/atomic/autoloader/index.esm.js', - 'esbuild src/autoloader/index.ts --format=cjs --outfile=dist/atomic/autoloader/index.cjs.js', - ]; - for (const command of commands) { - execSync(command, { - stdio: 'inherit', - env: {...process.env, IS_DEV: 'true'}, - }); - } - if (await checkPort(4400)) { - cpSync('./dist/atomic', './dist-storybook/assets', {recursive: true}); - cpSync('./dist/atomic/lang', './dist-storybook/lang', {recursive: true}); - } -} - -watch('src', {recursive: true}, rebuild); -rebuild(); diff --git a/packages/atomic/scripts/watch2.js b/packages/atomic/scripts/watch2.js deleted file mode 100644 index fb150f451b..0000000000 --- a/packages/atomic/scripts/watch2.js +++ /dev/null @@ -1,159 +0,0 @@ -import chalk from 'chalk'; -import {exec} from 'node:child_process'; -import {watch} from 'node:fs'; -// import util from 'node:util'; -import ora from 'ora'; - -let runningProcesses = []; -let isStopped = false; -let currentTask = null; - -// const execPromise = util.promisify(exec); - -function runCommand(command) { - return new Promise((resolve, reject) => { - if (isStopped) { - return resolve(); - } - - const process = exec(command); - runningProcesses.push(process); - - process.stdout.on('data', (data) => console.log(chalk.green(data))); - process.stderr.on('data', (data) => console.error(chalk.red(data))); - - process.on('close', (code, signal) => { - runningProcesses = runningProcesses.filter((p) => p !== process); - - if (isStopped || signal === 'SIGTERM' || signal === 'SIGINT') { - console.log(chalk.yellow(`⚠️ Process for "${command}" was stopped.`)); - return resolve(); - } - - if (code === 0) { - resolve(); - } else { - reject(new Error(`Command failed with exit code ${code}`)); - } - }); - - process.on('error', (err) => { - if (isStopped) { - return resolve(); - } - reject(err); - }); - }); -} - -async function funkyAnimation(text, action) { - if (isStopped) { - return Promise.resolve(); - } - - const frames = ['🍎', '🚀', '🔥', '💥', '✨', '🌟', '🎉', '🍏']; - const spinner = ora({ - text: text, - spinner: {frames, interval: 200}, - }).start(); - - try { - await action(); - spinner.succeed(chalk.magenta.bold(`${text} ✅`)); - } catch (error) { - spinner.fail(chalk.red.bold(`${text} ❌`)); - if (error.stdout) { - console.error(chalk.red(error.stdout)); - } - if (error.stderr) { - console.error(chalk.red(error.stderr)); - } - process.exit(1); - } -} - -async function stopAllProcesses() { - console.log(chalk.red('🛑 Stopping all running processes...')); - isStopped = true; - - runningProcesses.forEach((process) => { - if (process && process.kill) { - process.kill('SIGTERM'); - } - }); - - runningProcesses = []; - currentTask = null; - - // Wait briefly to ensure processes terminate before restarting the build - await new Promise((resolve) => setTimeout(resolve, 100)); -} - -watch('src', {recursive: true}, async (eventType, filename) => { - await stopAllProcesses(); // Ensure all previous tasks are stopped before continuing - - console.log(chalk.cyanBright(`📂 File changed: ${filename}`)); - console.log(chalk.yellowBright(`🔄 Event Type: ${eventType}`)); - - isStopped = false; // Allow execution again - - try { - currentTask = funkyAnimation('🍎 Rebuilding Stencil...'); - await currentTask; - if (isStopped) { - return; - } - - currentTask = runCommand( - 'node --max_old_space_size=6144 ../../node_modules/@stencil/core/bin/stencil build --tsConfig tsconfig.stencil.json' - ); - await currentTask; - if (isStopped) { - return; - } - - currentTask = funkyAnimation('🍏 Placing the Stencil Proxy...'); - await currentTask; - if (isStopped) { - return; - } - - currentTask = runCommand('node ./scripts/stencil-proxy.mjs'); - await currentTask; - if (isStopped) { - return; - } - - currentTask = funkyAnimation('🚀 Running esbuild for autoloader ESM...'); - await currentTask; - if (isStopped) { - return; - } - - currentTask = runCommand( - 'esbuild src/autoloader/index.ts --format=esm --outfile=dist/atomic/autoloader/index.esm.js' - ); - await currentTask; - if (isStopped) { - return; - } - - currentTask = funkyAnimation('🔥 Running esbuild for autoloader CJS...'); - await currentTask; - if (isStopped) { - return; - } - - currentTask = runCommand( - 'esbuild src/autoloader/index.ts --format=cjs --outfile=dist/atomic/autoloader/index.cjs.js' - ); - await currentTask; - if (isStopped) { - return; - } - - console.log(chalk.bold.bgMagenta(' 🎇 Build process completed! 🎇 ')); - } catch (error) { - console.log(chalk.red(`❌ Build process failed: ${error.message}`)); - } -}); diff --git a/packages/atomic/tsconfig.json b/packages/atomic/tsconfig.json index 92b1cf51ae..c3ad9b3846 100644 --- a/packages/atomic/tsconfig.json +++ b/packages/atomic/tsconfig.json @@ -27,10 +27,7 @@ "no-nullable-attribute-binding": "warning" } } - ], - "paths": { - "@/*": ["./*"] - } + ] }, "exclude": ["node_modules", "src/external-builds"], "include": ["src"] diff --git a/packages/atomic/tsconfig.lit.json b/packages/atomic/tsconfig.lit.json index e29793360e..8a2614d2db 100644 --- a/packages/atomic/tsconfig.lit.json +++ b/packages/atomic/tsconfig.lit.json @@ -9,10 +9,7 @@ "declarationMap": true, "experimentalDecorators": true, "useDefineForClassFields": false, - "outDir": "dist/atomic/components", - "paths": { - "@/*": ["./*"] - } + "outDir": "dist/atomic/components" }, "files": [ diff --git a/packages/atomic/tsconfig.stencil.json b/packages/atomic/tsconfig.stencil.json index 4aca66ed41..409bde9380 100644 --- a/packages/atomic/tsconfig.stencil.json +++ b/packages/atomic/tsconfig.stencil.json @@ -13,10 +13,7 @@ "noUnusedLocals": true, "noUnusedParameters": true, "jsx": "react", - "jsxFactory": "h", - "paths": { - "@/*": ["./*"] - } + "jsxFactory": "h" }, "references": [ { From aceab16a7491974a1f843b0d3382c81e9a180bf5 Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Fri, 31 Jan 2025 14:59:03 -0500 Subject: [PATCH 05/12] typescript path plugin https://coveord.atlassian.net/browse/KIT-3920 --- packages/atomic/.storybook/main.mts | 16 ------ packages/atomic/scripts/build.mjs | 3 +- .../component.ts.hbs | 2 +- packages/atomic/scripts/path-transform.mjs | 50 +++++++++++++++++++ .../generated-answer-common.tsx | 1 - .../facets/atomic-facet/atomic-facet.tsx | 2 - packages/atomic/tsconfig.json | 5 +- packages/atomic/tsconfig.storybook.json | 5 +- 8 files changed, 58 insertions(+), 26 deletions(-) create mode 100644 packages/atomic/scripts/path-transform.mjs diff --git a/packages/atomic/.storybook/main.mts b/packages/atomic/.storybook/main.mts index f73fde71a7..387e0eda2a 100644 --- a/packages/atomic/.storybook/main.mts +++ b/packages/atomic/.storybook/main.mts @@ -64,7 +64,6 @@ const config: StorybookConfig = { plugins: [ nxViteTsPaths(), resolveStorybookUtilsImports(), - resolveSrcImports(), forceInlineCssImports(), configType === 'PRODUCTION' && isCDN && externalizeDependencies(), ], @@ -89,21 +88,6 @@ const resolveStorybookUtilsImports: PluginImpl = () => { }; }; -const resolveSrcImports: PluginImpl = () => { - return { - name: 'resolve-storybook-utils-imports', - async resolveId(source: string, importer, options) { - if (source.startsWith('@/src')) { - return this.resolve( - source.replace('@/src', path.resolve(__dirname, '../src')), - importer, - options - ); - } - }, - }; -}; - const forceInlineCssImports: PluginImpl = () => { return { name: 'force-inline-css-imports', diff --git a/packages/atomic/scripts/build.mjs b/packages/atomic/scripts/build.mjs index 3ea239f5a7..95966cc280 100644 --- a/packages/atomic/scripts/build.mjs +++ b/packages/atomic/scripts/build.mjs @@ -9,6 +9,7 @@ import { createProgram, flattenDiagnosticMessageText, } from 'typescript'; +import pathTransformer from './path-transform.mjs'; import svgTransformer from './svg-transform.mjs'; const args = argv.slice(2); @@ -38,7 +39,7 @@ function emit(program) { const writeFile = undefined; const emitOnlyDtsFiles = false; const customTransformers = { - before: [svgTransformer], + before: [svgTransformer, pathTransformer], }; return program.emit( diff --git a/packages/atomic/scripts/generate-component-templates/component.ts.hbs b/packages/atomic/scripts/generate-component-templates/component.ts.hbs index b3dfe8f6c4..b3420bfc00 100644 --- a/packages/atomic/scripts/generate-component-templates/component.ts.hbs +++ b/packages/atomic/scripts/generate-component-templates/component.ts.hbs @@ -1,6 +1,6 @@ +import {TailwindLitElement} from '@/src/utils/tailwind.element'; import {CSSResultGroup, html, unsafeCSS} from 'lit'; import {customElement, property} from 'lit/decorators.js'; -import {TailwindLitElement} from '../../../utils/tailwind.element'; import styles from './{{name}}.css'; /** diff --git a/packages/atomic/scripts/path-transform.mjs b/packages/atomic/scripts/path-transform.mjs new file mode 100644 index 0000000000..a73d1136fb --- /dev/null +++ b/packages/atomic/scripts/path-transform.mjs @@ -0,0 +1,50 @@ +import {readFileSync} from 'fs'; +import {basename, dirname, join, resolve, relative} from 'path'; +import { + NodeFlags, + isImportDeclaration, + visitEachChild, + visitNode, + isStringLiteral, +} from 'typescript'; + +/** + * Custom SVG transformer to handle .svg imports. + */ +export default function pathTransformer(context) { + const {factory} = context; + + function visit(node, sourceFile) { + if (isImportDeclaration(node) && isStringLiteral(node.moduleSpecifier)) { + const importPath = node.moduleSpecifier.text; + + if (importPath.startsWith('@/')) { + const relativePath = getRelativeImportPath( + sourceFile.fileName, + importPath + ); + + console.log(relativePath); + + return factory.updateImportDeclaration( + node, + node.modifiers, + node.importClause, + factory.createStringLiteral(relativePath), + node.assertClause + ); + } + } + return visitEachChild(node, (child) => visit(child, sourceFile), context); + } + + return (sourceFile) => + visitNode(sourceFile, (node) => visit(node, sourceFile)); +} + +function getRelativeImportPath(sourceFilePath, importPath) { + const basePath = resolve(process.cwd()); + const absoluteImportPath = resolve(basePath, importPath.replace('@/', '')); + const relativePath = relative(dirname(sourceFilePath), absoluteImportPath); + return relativePath.startsWith('.') ? relativePath : `./${relativePath}`; +} diff --git a/packages/atomic/src/components/common/generated-answer/generated-answer-common.tsx b/packages/atomic/src/components/common/generated-answer/generated-answer-common.tsx index 751474e7e8..b2601fd0e7 100644 --- a/packages/atomic/src/components/common/generated-answer/generated-answer-common.tsx +++ b/packages/atomic/src/components/common/generated-answer/generated-answer-common.tsx @@ -419,7 +419,6 @@ export class GeneratedAnswerCommon { } public render() { - console.log('I did changes'); if (this.shouldBeHidden) { return null; } diff --git a/packages/atomic/src/components/search/facets/atomic-facet/atomic-facet.tsx b/packages/atomic/src/components/search/facets/atomic-facet/atomic-facet.tsx index 48a79140b2..6cd3b45a06 100644 --- a/packages/atomic/src/components/search/facets/atomic-facet/atomic-facet.tsx +++ b/packages/atomic/src/components/search/facets/atomic-facet/atomic-facet.tsx @@ -185,7 +185,6 @@ export class AtomicFacet implements InitializableComponent { */ @Prop({reflect: true}) public resultsMustMatch: FacetResultsMustMatch = 'atLeastOneValue'; - /** * Whether to display the facet values as checkboxes (multiple selection), links (single selection) or boxes (multiple selection). * Possible values are 'checkbox', 'link', and 'box'. @@ -349,7 +348,6 @@ export class AtomicFacet implements InitializableComponent { firstSearchExecuted={this.searchStatusState.firstSearchExecuted} hasResults={this.facetState.values.length > 0} > - poop changesss {this.searchStatusState.firstSearchExecuted ? ( Date: Fri, 31 Jan 2025 16:08:50 -0500 Subject: [PATCH 06/12] add back the web:dev nx command https://coveord.atlassian.net/browse/KIT-3920 --- packages/atomic/project.json | 7 +++++++ packages/atomic/scripts/dev.js | 1 - packages/atomic/scripts/path-transform.mjs | 5 ++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/atomic/project.json b/packages/atomic/project.json index e5b0342f3b..6f3de57201 100644 --- a/packages/atomic/project.json +++ b/packages/atomic/project.json @@ -100,6 +100,13 @@ "waitUntilTargets": ["^dev"] } }, + "web:dev": { + "executor": "nx:run-commands", + "options": { + "command": "node ./scripts/start-dev.mjs", + "cwd": "{projectRoot}" + } + }, "stencil:dev": { "dependsOn": ["^build", "build:locales"], "executor": "nx:run-commands", diff --git a/packages/atomic/scripts/dev.js b/packages/atomic/scripts/dev.js index 417d88ca4a..3025f67781 100644 --- a/packages/atomic/scripts/dev.js +++ b/packages/atomic/scripts/dev.js @@ -111,7 +111,6 @@ const [isStencil] = process.argv.slice(2); // Watch the src folder for changes watch('src', {recursive: true}, async (_, filename) => { - console.log(isStencil); await stopAllProcesses(); console.log(chalk.cyanBright(`📂 File changed: ${filename}`)); diff --git a/packages/atomic/scripts/path-transform.mjs b/packages/atomic/scripts/path-transform.mjs index a73d1136fb..99dc76f790 100644 --- a/packages/atomic/scripts/path-transform.mjs +++ b/packages/atomic/scripts/path-transform.mjs @@ -9,7 +9,8 @@ import { } from 'typescript'; /** - * Custom SVG transformer to handle .svg imports. + * //TODO Add a lot of jsdoc + * TypeScript transformer that replaces import paths starting with `@/` with relative paths. */ export default function pathTransformer(context) { const {factory} = context; @@ -24,8 +25,6 @@ export default function pathTransformer(context) { importPath ); - console.log(relativePath); - return factory.updateImportDeclaration( node, node.modifiers, From a4314690ab4cbbba5147c4bfb58847b3bf72d302 Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Mon, 3 Feb 2025 09:58:27 -0500 Subject: [PATCH 07/12] jsdoc and more comments in the dev https://coveord.atlassian.net/browse/KIT-3920 --- package-lock.json | 283 ++++++++++++++++++ packages/atomic/package.json | 2 + packages/atomic/project.json | 6 +- packages/atomic/scripts/{dev.js => dev.mjs} | 45 ++- ...te-component.js => generate-component.mjs} | 2 +- .../scripts/{start-dev.mjs => start-vite.mjs} | 0 packages/atomic/tsconfig.stencil.json | 5 +- 7 files changed, 331 insertions(+), 12 deletions(-) rename packages/atomic/scripts/{dev.js => dev.mjs} (71%) rename packages/atomic/scripts/{generate-component.js => generate-component.mjs} (97%) rename packages/atomic/scripts/{start-dev.mjs => start-vite.mjs} (100%) diff --git a/package-lock.json b/package-lock.json index 6ce338a111..fa0a9e0900 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55652,6 +55652,19 @@ "dev": true, "license": "MIT" }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/stencil-inline-svg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stencil-inline-svg/-/stencil-inline-svg-1.1.0.tgz", @@ -61008,6 +61021,7 @@ "@whitespace/storybook-addon-html": "6.1.1", "autoprefixer": "10.4.20", "axe-core": "4.10.2", + "chalk": "4.1.2", "cypress": "13.7.3", "cypress-axe": "1.5.0", "cypress-repeat": "2.3.8", @@ -61022,6 +61036,7 @@ "lit-html": "3.2.1", "local-web-server": "5.4.0", "natural-orderby": "5.0.0", + "ora": "8.2.0", "playwright": "1.50.0", "postcss": "8.5.1", "postcss-focus-visible": "10.0.1", @@ -62038,6 +62053,227 @@ "pretty-format": "^29.0.0" } }, + "packages/atomic/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "packages/atomic/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "packages/atomic/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "packages/atomic/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/atomic/node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/atomic/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "packages/atomic/node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/atomic/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/atomic/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/atomic/node_modules/log-symbols/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "packages/atomic/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/atomic/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/atomic/node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/atomic/node_modules/ora/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "packages/atomic/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/atomic/node_modules/rollup": { "version": "4.31.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.31.0.tgz", @@ -62077,6 +62313,53 @@ "fsevents": "~2.3.2" } }, + "packages/atomic/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/atomic/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "packages/atomic/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "packages/atomic/storybookUtils": { "extraneous": true }, diff --git a/packages/atomic/package.json b/packages/atomic/package.json index 04e6c67c0e..1e3e32ed38 100644 --- a/packages/atomic/package.json +++ b/packages/atomic/package.json @@ -119,6 +119,7 @@ "@whitespace/storybook-addon-html": "6.1.1", "autoprefixer": "10.4.20", "axe-core": "4.10.2", + "chalk": "4.1.2", "cypress": "13.7.3", "cypress-axe": "1.5.0", "cypress-repeat": "2.3.8", @@ -133,6 +134,7 @@ "lit-html": "3.2.1", "local-web-server": "5.4.0", "natural-orderby": "5.0.0", + "ora": "8.2.0", "playwright": "1.50.0", "postcss": "8.5.1", "postcss-focus-visible": "10.0.1", diff --git a/packages/atomic/project.json b/packages/atomic/project.json index 6f3de57201..a3f2d2104d 100644 --- a/packages/atomic/project.json +++ b/packages/atomic/project.json @@ -103,7 +103,7 @@ "web:dev": { "executor": "nx:run-commands", "options": { - "command": "node ./scripts/start-dev.mjs", + "command": "node ./scripts/start-vite.mjs", "cwd": "{projectRoot}" } }, @@ -162,7 +162,7 @@ "generate-component": { "executor": "nx:run-commands", "options": { - "command": "node ./scripts/generate-component.js {args.name} {args.output}", + "command": "node ./scripts/generate-component.mjs {args.name} {args.output}", "cwd": "{projectRoot}" }, "inputs": [], @@ -183,7 +183,7 @@ "dev": { "executor": "nx:run-commands", "options": { - "command": "node ./scripts/dev.js", + "command": "node ./scripts/dev.mjs {args.stencil}", "cwd": "{projectRoot}" }, "schema": { diff --git a/packages/atomic/scripts/dev.js b/packages/atomic/scripts/dev.mjs similarity index 71% rename from packages/atomic/scripts/dev.js rename to packages/atomic/scripts/dev.mjs index 3025f67781..52372fcf6f 100644 --- a/packages/atomic/scripts/dev.js +++ b/packages/atomic/scripts/dev.mjs @@ -4,10 +4,28 @@ import {watch} from 'node:fs'; import net from 'node:net'; import ora from 'ora'; +/** + * An array to keep track of running processes. + * @type {Array} + */ let runningProcesses = []; +/** + * A flag to stop the build process if a file changes during the build. + * @type {boolean} + */ let isStopped = false; +/** + * @type {import('node:child_process').ChildProcess} + */ let storybookServer; +/** + * Executes a command as a child process with a spinner animation. + * + * @param {string} text - The text to display alongside the spinner. + * @param {string} command - The command to execute in the child process. + * @returns {Promise} A promise that resolves when the command completes successfully, or rejects if an error occurs. + */ async function nextTask(text, command) { const frames = ['⚽ 🐕 ', ' ⚽ 🐕 ', ' ⚽🐕 ', ' ⚽🐕 ', '⚽ 🐕 ']; const spinner = ora({ @@ -18,6 +36,7 @@ async function nextTask(text, command) { return new Promise((resolve, reject) => { const childProcess = exec(command, {stdio: 'inherit'}); + // Add the process to the list of running processes to be able to stop them runningProcesses.push(childProcess); childProcess.on('exit', (code) => { @@ -50,9 +69,17 @@ async function stopAllProcesses() { runningProcesses = []; // Wait briefly to ensure processes terminate before restarting the build - await new Promise((resolve) => setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 300)); } +/** + * Waits for a specific port to be available on the given host. + * + * @param {number} port - The port number to check. + * @param {string} [host='localhost'] - The host to check the port on. + * @param {number} [timeout=30000] - The maximum time to wait for the port to be available, in milliseconds. + * @returns {Promise} A promise that resolves when the port is available, or rejects if the timeout is reached. + */ function waitForPort(port, host = 'localhost', timeout = 30000) { return new Promise((resolve, reject) => { const start = Date.now(); @@ -94,7 +121,7 @@ async function startServers() { }); // Script that starts the Vite server and copies files for CDN mode - exec('node ./scripts/start-dev.mjs', {stdio: 'ignore'}); + exec('node ./scripts/start-vite.mjs', {stdio: 'ignore'}); console.log( chalk.yellow('⌛ Waiting for Storybook (4400) and Vite (3333)...') @@ -104,16 +131,19 @@ async function startServers() { console.log(chalk.blue.bold('✅ Servers started! Watching for changes...')); } -// Start the servers first -startServers(); +// Get the stencil flag +const isStencil = process.argv.includes('--stencil'); -const [isStencil] = process.argv.slice(2); +// Start the servers (vite & storybook) first +startServers(); // Watch the src folder for changes watch('src', {recursive: true}, async (_, filename) => { + // Stop all processes if a file changes to prevent multiple builds at once await stopAllProcesses(); console.log(chalk.cyanBright(`📂 File changed: ${filename}`)); + // Flag to stop the build process if a file changes during the build isStopped = false; if (isStencil) { @@ -173,8 +203,9 @@ watch('src', {recursive: true}, async (_, filename) => { } await nextTask('Building storybook', 'npx storybook build -o dist-storybook'); - // restart storybook server, somehow even after build, it doesn't pick up the changes - // it needs a dev restart to pick up the changes + // Restart storybook server + // Somehow even after a build, the dev server doesn't pick up the changes. + // It needs a dev restart to pick them up. storybookServer.kill('SIGTERM'); exec('npx storybook dev -p 4400 --no-open', {stdio: 'ignore'}); diff --git a/packages/atomic/scripts/generate-component.js b/packages/atomic/scripts/generate-component.mjs similarity index 97% rename from packages/atomic/scripts/generate-component.js rename to packages/atomic/scripts/generate-component.mjs index 044d55dc74..d0e7110f22 100644 --- a/packages/atomic/scripts/generate-component.js +++ b/packages/atomic/scripts/generate-component.mjs @@ -90,7 +90,7 @@ if (outputDir) { if (!componentName) { console.error( - 'Usage: node generate-component.js []' + 'Usage: npx nx run atomic:generate-component --name= --output=' ); process.exit(1); } diff --git a/packages/atomic/scripts/start-dev.mjs b/packages/atomic/scripts/start-vite.mjs similarity index 100% rename from packages/atomic/scripts/start-dev.mjs rename to packages/atomic/scripts/start-vite.mjs diff --git a/packages/atomic/tsconfig.stencil.json b/packages/atomic/tsconfig.stencil.json index 409bde9380..4aca66ed41 100644 --- a/packages/atomic/tsconfig.stencil.json +++ b/packages/atomic/tsconfig.stencil.json @@ -13,7 +13,10 @@ "noUnusedLocals": true, "noUnusedParameters": true, "jsx": "react", - "jsxFactory": "h" + "jsxFactory": "h", + "paths": { + "@/*": ["./*"] + } }, "references": [ { From 7a529affe40cc86aa04676459af930699ba8d73f Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Mon, 3 Feb 2025 10:48:22 -0500 Subject: [PATCH 08/12] unneeded storybook plugin https://coveord.atlassian.net/browse/KIT-3920 --- packages/atomic/.storybook/main.mts | 20 ------------------- .../component.ts.hbs | 2 +- packages/atomic/tsconfig.lit.json | 5 ++++- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/packages/atomic/.storybook/main.mts b/packages/atomic/.storybook/main.mts index 387e0eda2a..7f0c30de95 100644 --- a/packages/atomic/.storybook/main.mts +++ b/packages/atomic/.storybook/main.mts @@ -64,7 +64,6 @@ const config: StorybookConfig = { plugins: [ nxViteTsPaths(), resolveStorybookUtilsImports(), - forceInlineCssImports(), configType === 'PRODUCTION' && isCDN && externalizeDependencies(), ], }), @@ -88,23 +87,4 @@ const resolveStorybookUtilsImports: PluginImpl = () => { }; }; -const forceInlineCssImports: PluginImpl = () => { - return { - name: 'force-inline-css-imports', - enforce: 'pre', - transform(code, id) { - if (id.endsWith('.ts')) { - return { - code: code.replace( - /import\s+([^'"]+)\s+from\s+['"]([^'"]+\.css)['"]/g, - (_, importName, cssPath) => - `import ${importName} from '${cssPath}?inline'` - ), - map: null, - }; - } - return null; - }, - }; -}; export default config; diff --git a/packages/atomic/scripts/generate-component-templates/component.ts.hbs b/packages/atomic/scripts/generate-component-templates/component.ts.hbs index b3420bfc00..4de0d6db4d 100644 --- a/packages/atomic/scripts/generate-component-templates/component.ts.hbs +++ b/packages/atomic/scripts/generate-component-templates/component.ts.hbs @@ -1,4 +1,4 @@ -import {TailwindLitElement} from '@/src/utils/tailwind.element'; +import {TailwindLitElement} from '@/src/utils/tailwind.element.js'; import {CSSResultGroup, html, unsafeCSS} from 'lit'; import {customElement, property} from 'lit/decorators.js'; import styles from './{{name}}.css'; diff --git a/packages/atomic/tsconfig.lit.json b/packages/atomic/tsconfig.lit.json index 8a2614d2db..e29793360e 100644 --- a/packages/atomic/tsconfig.lit.json +++ b/packages/atomic/tsconfig.lit.json @@ -9,7 +9,10 @@ "declarationMap": true, "experimentalDecorators": true, "useDefineForClassFields": false, - "outDir": "dist/atomic/components" + "outDir": "dist/atomic/components", + "paths": { + "@/*": ["./*"] + } }, "files": [ From 2f3aafc16947b3d77978eb1252d4bf169f07e41d Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Mon, 3 Feb 2025 11:04:15 -0500 Subject: [PATCH 09/12] fix multiple storybook probleme https://coveord.atlassian.net/browse/KIT-3920 --- packages/atomic/scripts/dev.mjs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/atomic/scripts/dev.mjs b/packages/atomic/scripts/dev.mjs index 52372fcf6f..e366d13bad 100644 --- a/packages/atomic/scripts/dev.mjs +++ b/packages/atomic/scripts/dev.mjs @@ -203,11 +203,14 @@ watch('src', {recursive: true}, async (_, filename) => { } await nextTask('Building storybook', 'npx storybook build -o dist-storybook'); + // Restart storybook server // Somehow even after a build, the dev server doesn't pick up the changes. // It needs a dev restart to pick them up. storybookServer.kill('SIGTERM'); - exec('npx storybook dev -p 4400 --no-open', {stdio: 'ignore'}); + storybookServer = exec('npx storybook dev -p 4400 --no-open', { + stdio: 'ignore', + }); if (isStopped) { return; From ef70295ccb02f5b4411b80f8f0bec75a1903b97c Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Mon, 3 Feb 2025 11:12:45 -0500 Subject: [PATCH 10/12] jsdoc for the path transformer https://coveord.atlassian.net/browse/KIT-3920 --- packages/atomic/scripts/path-transform.mjs | 30 +++++++++++++++------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/packages/atomic/scripts/path-transform.mjs b/packages/atomic/scripts/path-transform.mjs index 99dc76f790..52d54cac3d 100644 --- a/packages/atomic/scripts/path-transform.mjs +++ b/packages/atomic/scripts/path-transform.mjs @@ -1,16 +1,19 @@ -import {readFileSync} from 'fs'; -import {basename, dirname, join, resolve, relative} from 'path'; +import {dirname, resolve, relative} from 'path'; import { - NodeFlags, isImportDeclaration, visitEachChild, visitNode, isStringLiteral, } from 'typescript'; +// The import prefix as defined in the tsconfig under paths +const IMPORT_PREFIX = '@/'; + /** - * //TODO Add a lot of jsdoc - * TypeScript transformer that replaces import paths starting with `@/` with relative paths. + * Transforms import paths in TypeScript source files. + * + * @param {import('typescript').TransformationContext} context - The transformation context provided by TypeScript. + * @returns {import('typescript').Transformer} A transformer function that processes a source file. */ export default function pathTransformer(context) { const {factory} = context; @@ -19,7 +22,7 @@ export default function pathTransformer(context) { if (isImportDeclaration(node) && isStringLiteral(node.moduleSpecifier)) { const importPath = node.moduleSpecifier.text; - if (importPath.startsWith('@/')) { + if (importPath.startsWith(IMPORT_PREFIX)) { const relativePath = getRelativeImportPath( sourceFile.fileName, importPath @@ -29,8 +32,7 @@ export default function pathTransformer(context) { node, node.modifiers, node.importClause, - factory.createStringLiteral(relativePath), - node.assertClause + factory.createStringLiteral(relativePath) ); } } @@ -41,9 +43,19 @@ export default function pathTransformer(context) { visitNode(sourceFile, (node) => visit(node, sourceFile)); } +/** + * Generates a relative import path from a source file path and an import path. + * + * @param {string} sourceFilePath - The file path of the source file. + * @param {string} importPath - The import path to be transformed. + * @returns {string} The relative import path. + */ function getRelativeImportPath(sourceFilePath, importPath) { const basePath = resolve(process.cwd()); - const absoluteImportPath = resolve(basePath, importPath.replace('@/', '')); + const absoluteImportPath = resolve( + basePath, + importPath.replace(IMPORT_PREFIX, '') + ); const relativePath = relative(dirname(sourceFilePath), absoluteImportPath); return relativePath.startsWith('.') ? relativePath : `./${relativePath}`; } From 2faf2851fb358c4e8c03d86ff0e4ee42eceb938f Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Mon, 3 Feb 2025 11:29:55 -0500 Subject: [PATCH 11/12] last fixes https://coveord.atlassian.net/browse/KIT-3920 --- packages/atomic/project.json | 2 +- packages/atomic/scripts/dev.mjs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/atomic/project.json b/packages/atomic/project.json index a3f2d2104d..28db8fb7e6 100644 --- a/packages/atomic/project.json +++ b/packages/atomic/project.json @@ -183,7 +183,7 @@ "dev": { "executor": "nx:run-commands", "options": { - "command": "node ./scripts/dev.mjs {args.stencil}", + "command": "node ./scripts/dev.mjs", "cwd": "{projectRoot}" }, "schema": { diff --git a/packages/atomic/scripts/dev.mjs b/packages/atomic/scripts/dev.mjs index e366d13bad..8faa114192 100644 --- a/packages/atomic/scripts/dev.mjs +++ b/packages/atomic/scripts/dev.mjs @@ -202,7 +202,10 @@ watch('src', {recursive: true}, async (_, filename) => { return; } - await nextTask('Building storybook', 'npx storybook build -o dist-storybook'); + await nextTask( + 'Building storybook...', + 'npx storybook build -o dist-storybook' + ); // Restart storybook server // Somehow even after a build, the dev server doesn't pick up the changes. From 4859d9a198a0037fd6d715953132ba8d23fff3f9 Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Tue, 4 Feb 2025 08:47:43 -0500 Subject: [PATCH 12/12] use wait-on insted https://coveord.atlassian.net/browse/KIT-3920 --- packages/atomic/scripts/dev.mjs | 46 ++++++++++----------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/packages/atomic/scripts/dev.mjs b/packages/atomic/scripts/dev.mjs index 8faa114192..de164f968e 100644 --- a/packages/atomic/scripts/dev.mjs +++ b/packages/atomic/scripts/dev.mjs @@ -3,6 +3,7 @@ import {exec} from 'node:child_process'; import {watch} from 'node:fs'; import net from 'node:net'; import ora from 'ora'; +import waitOn from 'wait-on'; /** * An array to keep track of running processes. @@ -73,44 +74,25 @@ async function stopAllProcesses() { } /** - * Waits for a specific port to be available on the given host. + * Waits for a specific port to be available on the given host using wait-on. * * @param {number} port - The port number to check. * @param {string} [host='localhost'] - The host to check the port on. * @param {number} [timeout=30000] - The maximum time to wait for the port to be available, in milliseconds. * @returns {Promise} A promise that resolves when the port is available, or rejects if the timeout is reached. */ -function waitForPort(port, host = 'localhost', timeout = 30000) { - return new Promise((resolve, reject) => { - const start = Date.now(); - - function check() { - const socket = net.createConnection(port, host); - socket.setTimeout(1000); - socket.on('connect', () => { - socket.destroy(); - resolve(); - }); - socket.on('timeout', () => { - socket.destroy(); - if (Date.now() - start > timeout) { - reject(new Error(`Timeout waiting for port ${port}`)); - } else { - setTimeout(check, 500); - } - }); - socket.on('error', () => { - socket.destroy(); - if (Date.now() - start > timeout) { - reject(new Error(`Timeout waiting for port ${port}`)); - } else { - setTimeout(check, 500); - } - }); - } - - check(); - }); +async function waitForPort(port, host = 'localhost', timeout = 30000) { + const resource = `tcp:${host}:${port}`; + try { + await waitOn({ + resources: [resource], + timeout, + tcpTimeout: 1000, + interval: 500, + }); + } catch (error) { + throw new Error(`Timeout waiting for port ${port} on ${host}`); + } } async function startServers() {