diff --git a/package.json b/package.json index e7094b8c2..7a24e737e 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "cross-env": "^7.0.3", "eslint": "^9.18.0", "eslint-plugin-playwright": "^2.1.0", + "execa": "^9.5.2", "fast-glob": "^3.3.3", "get-tsconfig": "^4.8.1", "jest-extended": "^4.0.2", @@ -41,7 +42,6 @@ "js-beautify": "^1.15.1", "nanoid": "^4.0.2", "shuffle-array": "^1.0.1", - "spawnd": "^9.0.2", "strip-ansi": "^7.1.0", "techor": "^3.1.7", "tinybench": "^3.1.0", diff --git a/packages/cli/tests/extract/watch/test.ts b/packages/cli/tests/extract/watch/test.ts index 9fcbd5369..66e84e3ca 100644 --- a/packages/cli/tests/extract/watch/test.ts +++ b/packages/cli/tests/extract/watch/test.ts @@ -6,7 +6,7 @@ import cssEscape from 'shared/utils/css-escape' import waitForDataMatch from 'shared/utils/wait-for-data-match' import dedent from 'ts-dedent' import { it, beforeAll, afterAll, expect } from 'vitest' -import { spawnd, SpawndChildProcess } from 'spawnd' +import { execa, ExecaChildProcess } from 'execa' const HTMLFilepath = path.join(__dirname, 'test.html') const originHTMLText = dedent` @@ -51,19 +51,19 @@ export default config const virtualCSSFilepath = path.join(__dirname, '.virtual/master.css') -let child: SpawndChildProcess +let subprocess: ExecaChildProcess beforeAll(() => { fs.writeFileSync(HTMLFilepath, originHTMLText, { flag: 'w+' }) fs.writeFileSync(optionsFilepath, originOptionsText, { flag: 'w+' }) fs.writeFileSync(configFilepath, originConfigText, { flag: 'w+' }) - child = spawnd('tsx ../../../src/bin extract -w', { shell: true, cwd: __dirname }) + subprocess = execa('tsx ../../../src/bin extract -w', { shell: true, cwd: __dirname }) }, 120000) it('start watch process', async () => { await Promise.all([ - waitForDataMatch(child, (data) => data.includes('Start watching source changes')), - waitForDataMatch(child, (data) => data.includes('exported')) + waitForDataMatch(subprocess, (data) => data.includes('Start watching source changes')), + waitForDataMatch(subprocess, (data) => data.includes('exported')) ]) const fileCSSText = fs.readFileSync(virtualCSSFilepath, { encoding: 'utf8' }) expect(fileCSSText).toContain(cssEscape('font:heavy')) @@ -74,11 +74,11 @@ it('start watch process', async () => { it('change options file `fixed` and reset process', async () => { await Promise.all([ - waitForDataMatch(child, (data) => data.includes('watching source changes'), async () => { + waitForDataMatch(subprocess, (data) => data.includes('watching source changes'), async () => { fs.writeFileSync(optionsFilepath, originOptionsText.replace('fixed: []', 'fixed: [\'fg:red\']')) }), - waitForDataMatch(child, (data) => data.includes(`inserted 'fg:red'`)), - waitForDataMatch(child, (data) => data.includes('exported')), + waitForDataMatch(subprocess, (data) => data.includes(`inserted 'fg:red'`)), + waitForDataMatch(subprocess, (data) => data.includes('exported')), ]) const fileCSSText = fs.readFileSync(virtualCSSFilepath, { encoding: 'utf8' }) expect(fileCSSText).toContain(cssEscape('fg:red')) @@ -86,10 +86,10 @@ it('change options file `fixed` and reset process', async () => { it('change config file `styles` and reset process', async () => { await Promise.all([ - waitForDataMatch(child, (data) => data.includes('watching source changes'), async () => { + waitForDataMatch(subprocess, (data) => data.includes('watching source changes'), async () => { fs.writeFileSync(configFilepath, originConfigText.replace('bg:red', 'bg:blue')) }), - waitForDataMatch(child, (data) => data.includes('exported')) + waitForDataMatch(subprocess, (data) => data.includes('exported')) ]) const fileCSSText = fs.readFileSync(virtualCSSFilepath, { encoding: 'utf8' }) expect(fileCSSText).toContain('.btn{background-color:rgb(var(--blue))') @@ -97,16 +97,16 @@ it('change config file `styles` and reset process', async () => { it('change html file class attr and update', async () => { await Promise.all([ - waitForDataMatch(child, (data) => data.includes('watching source changes'), () => { + waitForDataMatch(subprocess, (data) => data.includes('watching source changes'), () => { fs.writeFileSync(HTMLFilepath, originHTMLText.replace('hmr-test', 'text:underline')) }), - waitForDataMatch(child, (data) => data.includes(`classes inserted`)), - waitForDataMatch(child, (data) => data.includes('exported')) + waitForDataMatch(subprocess, (data) => data.includes(`classes inserted`)), + waitForDataMatch(subprocess, (data) => data.includes('exported')) ]) const fileCSSText = fs.readFileSync(virtualCSSFilepath, { encoding: 'utf8' }) expect(fileCSSText).toContain(cssEscape('text:underline')) }, 120000) afterAll(async () => { - await child.destroy() + subprocess.kill() }, 120000) \ No newline at end of file diff --git a/packages/extractor.vite/tests/nuxt.js/dev.test.ts b/packages/extractor.vite/tests/nuxt.js/dev.test.ts index 89bf3a64f..fe5788d25 100644 --- a/packages/extractor.vite/tests/nuxt.js/dev.test.ts +++ b/packages/extractor.vite/tests/nuxt.js/dev.test.ts @@ -4,13 +4,13 @@ import path from 'path' import cssEscape from 'shared/utils/css-escape' import puppeteer, { type Browser, type Page } from 'puppeteer-core' import { copy } from 'shared/utils/fs' -import { SpawndChildProcess, spawnd } from 'spawnd' import waitForDataMatch from 'shared/utils/wait-for-data-match' +import { execa, ExecaChildProcess } from 'execa' const examplePath = path.join(__dirname, '../../../../examples/nuxt.js-with-static-extraction') const tmpDir = path.join(__dirname, 'tmp/dev') -let devProcess: SpawndChildProcess +let devProcess: ExecaChildProcess let browser: Browser let page: Page let error: Error @@ -24,7 +24,7 @@ test.todo('nuxt.js dev tests timeout in CI', () => { templatePath = path.join(tmpDir, 'app.vue') templateContent = fs.readFileSync(templatePath).toString() masterCSSConfigPath = path.join(tmpDir, 'master.css.ts') - devProcess = spawnd('pnpm dev', { shell: true, cwd: tmpDir, env: { ...process.env, NODE_ENV: 'development' } }) + devProcess = execa('pnpm dev', { shell: true, cwd: tmpDir, env: { ...process.env, NODE_ENV: 'development' } }) const urlPattern = /(http:\/\/localhost:).*?([0-9]+)/ const data = await waitForDataMatch(devProcess, (data) => urlPattern.exec(data)?.length) const result = urlPattern.exec(data) @@ -76,6 +76,6 @@ test.todo('nuxt.js dev tests timeout in CI', () => { afterAll(async () => { await page.close() await browser.close() - await devProcess.destroy() + devProcess.kill() }, 60000) }) \ No newline at end of file diff --git a/packages/extractor.vite/tests/react/dev.test.ts b/packages/extractor.vite/tests/react/dev.test.ts index 81cad1d25..21966a611 100644 --- a/packages/extractor.vite/tests/react/dev.test.ts +++ b/packages/extractor.vite/tests/react/dev.test.ts @@ -4,16 +4,15 @@ import path from 'path' import cssEscape from 'shared/utils/css-escape' import puppeteer, { type Browser, type Page } from 'puppeteer-core' import { copy, rm } from 'shared/utils/fs' -import { SpawndChildProcess, spawnd } from 'spawnd' +import { execa, ExecaChildProcess } from 'execa' import waitForDataMatch from 'shared/utils/wait-for-data-match' -import delay from 'shared/utils/delay' test.todo('react dev tests timeout in CI') if (!process.env.GITHUB_ACTIONS) { const examplePath = path.join(__dirname, '../../../../examples/react-with-static-extraction') const tmpDir = path.join(__dirname, 'tmp/dev') - let devProcess: SpawndChildProcess + let devProcess: ExecaChildProcess let browser: Browser let page: Page let error: Error @@ -26,7 +25,7 @@ if (!process.env.GITHUB_ACTIONS) { templatePath = path.join(tmpDir, 'src/App.tsx') templateContent = fs.readFileSync(templatePath).toString() masterCSSConfigPath = path.join(tmpDir, 'master.css.ts') - devProcess = spawnd('pnpm dev --port 4003', { shell: true, cwd: tmpDir }) + devProcess = execa('pnpm dev --port 4003', { shell: true, cwd: tmpDir }) const urlPattern = /(http:\/\/localhost:).*?([0-9]+)/ const data = await waitForDataMatch(devProcess, (data) => urlPattern.exec(data)?.length) const result = urlPattern.exec(data) @@ -77,6 +76,6 @@ if (!process.env.GITHUB_ACTIONS) { afterAll(async () => { await page.close() await browser.close() - await devProcess.destroy() + devProcess.kill() }, 60000) } \ No newline at end of file diff --git a/packages/extractor.vite/tests/vite/dev.test.ts b/packages/extractor.vite/tests/vite/dev.test.ts index 76c4deb19..c7dc5032d 100644 --- a/packages/extractor.vite/tests/vite/dev.test.ts +++ b/packages/extractor.vite/tests/vite/dev.test.ts @@ -4,16 +4,15 @@ import path from 'path' import cssEscape from 'shared/utils/css-escape' import puppeteer, { type Browser, type Page } from 'puppeteer-core' import { copy, rm } from 'shared/utils/fs' -import { SpawndChildProcess, spawnd } from 'spawnd' import waitForDataMatch from 'shared/utils/wait-for-data-match' -import delay from 'shared/utils/delay' +import { execa, ExecaChildProcess } from 'execa' test.todo('vite dev tests timeout in CI') if (!process.env.GITHUB_ACTIONS) { const examplePath = path.join(__dirname, '../../../../examples/vite-with-static-extraction') const tmpDir = path.join(__dirname, 'tmp/dev') - let devProcess: SpawndChildProcess + let devProcess: ExecaChildProcess let browser: Browser let page: Page let error: Error @@ -26,7 +25,7 @@ if (!process.env.GITHUB_ACTIONS) { templatePath = path.join(tmpDir, 'index.html') templateContent = fs.readFileSync(templatePath).toString() masterCSSConfigPath = path.join(tmpDir, 'master.css.ts') - devProcess = spawnd('pnpm dev --port 4005', { shell: true, cwd: tmpDir }) + devProcess = execa('pnpm dev --port 4005', { shell: true, cwd: tmpDir }) const urlPattern = /(http:\/\/localhost:).*?([0-9]+)/ const data = await waitForDataMatch(devProcess, (data) => urlPattern.exec(data)?.length) const result = urlPattern.exec(data) @@ -76,6 +75,6 @@ if (!process.env.GITHUB_ACTIONS) { afterAll(async () => { await page.close() await browser.close() - await devProcess.destroy() + devProcess.kill() }, 60000) } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eb9915a91..e5fa52fe5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: eslint-plugin-playwright: specifier: ^2.1.0 version: 2.1.0(eslint@9.18.0(jiti@2.4.2)) + execa: + specifier: ^9.5.2 + version: 9.5.2 fast-glob: specifier: ^3.3.3 version: 3.3.3 @@ -68,9 +71,6 @@ importers: shuffle-array: specifier: ^1.0.1 version: 1.0.1 - spawnd: - specifier: ^9.0.2 - version: 9.0.2 strip-ansi: specifier: ^7.1.0 version: 7.1.0 @@ -15657,10 +15657,6 @@ packages: spawn-sync@2.0.0: resolution: {integrity: sha512-AGXIhH/XZVinFewojYTsG8uapHX2e7MjtFbmibvK9qqG4qGd9b6jelU1sTkCA0RVGHvN9exJYTBVbF1Ls2f69g==} - spawnd@9.0.2: - resolution: {integrity: sha512-nl8DVHEDQ57IcKakzpjanspVChkMpGLuVwMR/eOn9cXE55Qr6luD2Kn06sA0ootRMdgrU4tInN6lA6ohTNvysw==} - engines: {node: '>=16'} - spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -37854,11 +37850,6 @@ snapshots: spawn-sync@2.0.0: {} - spawnd@9.0.2: - dependencies: - signal-exit: 4.1.0 - tree-kill: 1.2.2 - spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 diff --git a/shared/utils/wait-for-data-match.ts b/shared/utils/wait-for-data-match.ts index 2557da981..51b1efe15 100644 --- a/shared/utils/wait-for-data-match.ts +++ b/shared/utils/wait-for-data-match.ts @@ -1,26 +1,26 @@ -import { SpawndChildProcess } from 'spawnd' import stripAnsi from 'strip-ansi' +import { ExecaChildProcess } from 'execa' -export default function (child: SpawndChildProcess, doesDataMatch: (data: string) => any, onReady?: () => void): Promise { +export default function (child: ExecaChildProcess, doesDataMatch: (data: string) => any, onReady?: () => void): Promise { return new Promise((resolve, reject) => { const handler = (data: any) => { const strippedData = stripAnsi(data.toString()) if (doesDataMatch(strippedData)) { - child.stdout.off('data', handler) - child.stderr.off('data', errorHandler) + child?.stdout?.off('data', handler) + child?.stderr?.off('data', errorHandler) resolve(strippedData) } } const errorHandler = (data: any) => { const strippedData = stripAnsi(data.toString().replace(/(?:\r\n|\n|\r)/g, '')) if (strippedData) { - child.stdout.off('data', handler) - child.stderr.off('data', errorHandler) + child?.stdout?.off('data', handler) + child?.stderr?.off('data', errorHandler) reject(strippedData) } } - child.stdout.on('data', handler) - child.stderr.on('data', errorHandler) + child?.stdout?.on('data', handler) + child?.stderr?.on('data', errorHandler) onReady?.() }) } \ No newline at end of file