diff --git a/.gitignore b/.gitignore index 486eb366..c76a91ad 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ yarn.lock vite.config.js.timestamp-* /packages/create-svelte/template/CHANGELOG.md .test-tmp +.outputs-e2e +.outputs-snapshots diff --git a/.prettierignore b/.prettierignore index ea3293fe..5880e91c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,3 @@ packages/core/tests/**/input.* packages/core/tests/**/output.* +packages/adder-tests/_snapshots diff --git a/community-adder-template/package.json b/community-adder-template/package.json index 91f9b34c..44c49d78 100644 --- a/community-adder-template/package.json +++ b/community-adder-template/package.json @@ -13,13 +13,17 @@ ], "scripts": { "start": "sv add -C temp --community file:../", - "create-temp": "sv create temp --check-types typescript --template skeleton --no-adders --no-install" + "create-temp": "sv create temp --check-types typescript --template skeleton --no-adders --no-install", + "dev:test": "vitest", + "test": "vitest run" }, "dependencies": { "@sveltejs/cli-core": "workspace:*" }, "devDependencies": { - "sv": "workspace:*" + "@sveltejs/adder-testing-library": "workspace:*", + "sv": "workspace:*", + "vitest": "^2.1.2" }, "files": [ "src", diff --git a/community-adder-template/src/index.js b/community-adder-template/src/index.js index 9e4348aa..826a7261 100644 --- a/community-adder-template/src/index.js +++ b/community-adder-template/src/index.js @@ -3,14 +3,14 @@ import { imports } from '@sveltejs/cli-core/js'; import { parseScript } from '@sveltejs/cli-core/parsers'; export const options = defineAdderOptions({ - demo: { - question: 'Do you want to use a demo?', - type: 'boolean', - default: false - } + demo: { + question: 'Do you want to use a demo?', + type: 'boolean', + default: false + } }); -export const adder = defineAdder({ +export default defineAdder({ id: 'community-adder-template', environments: { kit: true, svelte: true }, options, diff --git a/community-adder-template/tests/end2end.js b/community-adder-template/tests/end2end.js new file mode 100644 index 00000000..706ae11d --- /dev/null +++ b/community-adder-template/tests/end2end.js @@ -0,0 +1,13 @@ +import { test, describe, beforeAll, afterAll } from 'vitest'; +import { runEndToEndTests } from '@sveltejs/adder-testing-library'; +import adder from '../src/index.js'; +import { tests } from './tests.js'; + +runEndToEndTests( + '.outputs-e2e', + [{ config: adder, tests }], + describe, + test.concurrent, + beforeAll, + afterAll +); diff --git a/community-adder-template/tests/snapshot.js b/community-adder-template/tests/snapshot.js new file mode 100644 index 00000000..cadecc66 --- /dev/null +++ b/community-adder-template/tests/snapshot.js @@ -0,0 +1,14 @@ +import { test, describe, beforeAll, afterAll } from 'vitest'; +import { runSnapshotTests } from '@sveltejs/adder-testing-library'; +import adder from '../src/index.js'; +import { tests } from './tests.js'; + +runSnapshotTests( + '.outputs-snapshots', + '_snapshot', + [{ config: adder, tests }], + describe, + test.concurrent, + beforeAll, + afterAll +); diff --git a/community-adder-template/tests/tests.js b/community-adder-template/tests/tests.js index 373c556b..7bbefcc2 100644 --- a/community-adder-template/tests/tests.js +++ b/community-adder-template/tests/tests.js @@ -2,9 +2,20 @@ import { defineAdderTests } from '@sveltejs/cli-core'; import { options } from '../src/index.js'; export const tests = defineAdderTests({ - files: [], options, optionValues: [{ demo: true }], + files: [ + { + name: ({ kit }) => `${kit?.routesDirectory}/+page.svelte`, + condition: ({ kit }) => Boolean(kit), + content: () => '
' + }, + { + name: () => 'src/App.svelte', + condition: ({ kit }) => !kit, + content: () => '
' + } + ], tests: [ { name: 'demo test', diff --git a/community-adder-template/vitest.config.ts b/community-adder-template/vitest.config.ts new file mode 100644 index 00000000..e8faff1d --- /dev/null +++ b/community-adder-template/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineConfig, type UserConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['./tests/**/*.js'], + exclude: ['./tests/tests.js'], + testTimeout: 1000 * 60 * 2, // 2 minutes + hookTimeout: 1000 * 60 * 3, // 3 minutes + pool: 'threads' + } +}) as UserConfig; diff --git a/eslint.config.js b/eslint.config.js index c6f1bbf0..f128499f 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -31,6 +31,8 @@ export default [ '.test-tmp/**/*', '**/dist/*', 'packages/**/tests/**/{output,input}.ts', + 'packages/**/.outputs-*/**/*', + 'packages/**/_snapshots/**/*', 'rollup.config.js' ] } diff --git a/package.json b/package.json index 3678c39b..2fce6314 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,20 @@ { "name": "sv-monorepo", - "version": "0.0.1", - "description": "monorepo for sv and friends", "private": true, + "version": "0.0.1", "type": "module", + "description": "monorepo for sv and friends", + "engines": { + "pnpm": "^9.0.0" + }, "scripts": { + "build": "rollup -c", + "changeset:publish": "changeset publish", "check": "pnpm --parallel check", - "lint": "pnpm --parallel lint && eslint --cache --cache-location node_modules/.eslintcache", - "format": "pnpm --parallel format", "dev": "rollup --config --watch", - "build": "rollup -c", - "test": "pnpm --parallel test", - "changeset:publish": "changeset publish" + "format": "pnpm --parallel format", + "lint": "pnpm --parallel lint && eslint --cache --cache-location node_modules/.eslintcache", + "test": "pnpm --parallel test" }, "devDependencies": { "@changesets/cli": "^2.27.9", @@ -23,7 +26,7 @@ "@sveltejs/eslint-config": "^8.1.0", "@svitejs/changesets-changelog-github-compact": "^1.1.0", "@types/node": "^22.3.0", - "@vitest/ui": "^2.0.5", + "@vitest/ui": "^2.1.2", "eslint": "^9.10.0", "magic-string": "^0.30.11", "prettier": "^3.3.3", @@ -37,10 +40,7 @@ "typescript": "^5.6.2", "typescript-eslint": "^8.5.0", "unplugin-isolated-decl": "^0.6.5", - "vitest": "^2.0.5" + "vitest": "^2.1.2" }, - "packageManager": "pnpm@9.7.0", - "engines": { - "pnpm": "^9.0.0" - } + "packageManager": "pnpm@9.7.0" } diff --git a/packages/adder-testing-library/browser.ts b/packages/adder-testing-library/browser.ts new file mode 100644 index 00000000..0d54ca0f --- /dev/null +++ b/packages/adder-testing-library/browser.ts @@ -0,0 +1,27 @@ +import { chromium, type Browser, type Page } from 'playwright'; + +let browser: Browser; +const headless = true; + +export async function startBrowser(): Promise { + browser = await chromium.launch({ headless }); + console.log('browser started'); +} + +export async function openPage(url: string): Promise { + const page = await browser.newPage(); + + await page.goto(url, { timeout: 60_000 }); + await page.waitForLoadState('networkidle'); + + // always use light mode. Otherwise the tests might depend on the OS setting + // of each developer and thus leads to inconsistent test results. + await page.emulateMedia({ colorScheme: 'light' }); + + return page; +} + +export async function stopBrowser(): Promise { + if (!browser) return; + await browser.close(); +} diff --git a/packages/adder-testing-library/index.ts b/packages/adder-testing-library/index.ts new file mode 100644 index 00000000..f9778fe4 --- /dev/null +++ b/packages/adder-testing-library/index.ts @@ -0,0 +1,168 @@ +import type { AdderTestConfig, AdderWithoutExplicitArgs, TestType } from '@sveltejs/cli-core'; +import path from 'node:path'; +import fs from 'node:fs'; +import process from 'node:process'; +import { openPage, stopBrowser } from './browser.ts'; +import { + generateTestCases, + prepareEndToEndTests, + prepareSnapshotTests, + runAdder, + startDevServer, + stopDevServer, + type TestCase +} from './utils.ts'; +import { startTests } from './tests.ts'; + +const templatesDirectoryName = 'templates'; +const addersDirectoryName = 'adders'; + +export type AdderWithTests = { + config: AdderWithoutExplicitArgs; + tests: AdderTestConfig>; +}; + +export function runEndToEndTests( + outputDirectory: string, + adders: AdderWithTests[], + describe: (name: string, testFactory: () => void) => void, + test: (name: string, testFunction: (args: TestArguments) => Promise | void) => void, + beforeAll: (fn: () => void) => void, + afterAll: (fn: () => void) => void +): void { + const outputPath = path.join(process.cwd(), outputDirectory); + const templatesPath = path.join(outputPath, templatesDirectoryName); + const addersOutputPath = path.join(outputPath, addersDirectoryName); + const testCases = generateTestCases(adders, addersOutputPath, { ignoreEmptyTests: true }); + + runTests(adders, testCases, 'end2end', { + describe, + test, + beforeAll, + afterAll, + prepare: async () => { + await prepareEndToEndTests(outputPath, templatesPath, addersOutputPath, adders, testCases); + }, + run: async (testCase, adder) => { + const cmd = adder.tests!.command ?? 'dev'; + const { url, devServer } = await startDevServer(testCase.cwd, cmd); + const page = await openPage(url); + + try { + const errorOcurred = await page.$('vite-error-overlay'); + if (errorOcurred) + throw new Error('Dev server failed to start correctly. Vite errors present'); + + await startTests(page, adder, testCase.options); + } finally { + await page.close(); + await stopDevServer(devServer); + } + }, + tearDown: async () => { + await stopBrowser(); + } + }); +} + +type TestArguments = { + expect: (content: string) => { + toMatchFileSnapshot: (filePath: string, message?: string) => void; + }; +}; + +export function runSnapshotTests( + outputDirectory: string, + snapshotDirectory: string, + adders: AdderWithTests[], + describe: (name: string, testFactory: () => void) => void, + test: (name: string, testFunction: (args: TestArguments) => Promise | void) => void, + beforeAll: (fn: () => void) => void, + afterAll: (fn: () => void) => void +): void { + const outputPath = path.join(process.cwd(), outputDirectory); + const templatesPath = path.join(outputPath, templatesDirectoryName); + const addersOutputPath = path.join(outputPath, addersDirectoryName); + const testCases = generateTestCases(adders, addersOutputPath, { ignoreEmptyTests: false }); + + runTests(adders, testCases, 'snapshot', { + describe, + test, + beforeAll, + afterAll, + prepare: async () => { + await prepareSnapshotTests(outputPath, templatesPath, addersOutputPath, testCases); + }, + run: (testCase, _, { expect }) => { + const filesToFormat = runAdder(testCase.adder, testCase.cwd, testCase.options, adders); + + for (const changedFile of filesToFormat) { + const fullFilePath = path.join(testCase.cwd, changedFile); + const content = fs.readFileSync(fullFilePath).toString(); + + const relativeTestCasePath = testCase.cwd.replace(addersOutputPath, ''); + const snapshotPath = path.join( + process.cwd(), + snapshotDirectory, + relativeTestCasePath, + changedFile + ); + + expect(content).toMatchFileSnapshot(snapshotPath, changedFile); + } + }, + tearDown: async () => {} + }); +} + +function runTests( + adders: AdderWithTests[], + testCases: Map, + testType: TestType, + options: { + describe: (name: string, testFactory: () => void) => void; + test: (name: string, testFunction: (args: TestArguments) => Promise | void) => void; + beforeAll: (fn: () => void) => void; + afterAll: (fn: () => void) => void; + prepare: () => Promise; + run: (testCase: TestCase, adder: AdderWithTests, args: TestArguments) => Promise | void; + tearDown: () => Promise; + } +) { + options.beforeAll(async () => { + await options.prepare(); + }); + + for (const [adderId, adderTestCases] of testCases) { + options.describe(adderId, () => { + const adder = adders.find((x) => x.config.id == adderId)!; + if (!adder) throw new Error('failed to find ' + adderId); + const adderTestDetails = adder.tests!; + options.beforeAll(async () => { + if (adderTestDetails.beforeAll) await adderTestDetails.beforeAll(testType); + }); + + for (const testCase of adderTestCases) { + options.test(testCase.testName, async (testArgs) => { + if (!adder.tests) return; + + if (adder.tests.beforeEach) await adder.tests.beforeEach(testCase.cwd, testType); + + try { + await options.run(testCase, adder, testArgs); + } finally { + if (adder.tests.afterEach) await adder.tests.afterEach(testCase.cwd, testType); + } + }); + } + + options.afterAll(async () => { + if (adderTestDetails.afterAll) await adderTestDetails.afterAll(testType); + }); + }); + } + + options.afterAll(async () => { + await options.tearDown(); + }); +} diff --git a/packages/adder-testing-library/package.json b/packages/adder-testing-library/package.json new file mode 100644 index 00000000..1bbdb47e --- /dev/null +++ b/packages/adder-testing-library/package.json @@ -0,0 +1,28 @@ +{ + "name": "@sveltejs/adder-testing-library", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "check": "tsc", + "format": "pnpm lint --write", + "postinstall": "pnpm exec playwright install chromium", + "lint": "prettier --check . --config ../../.prettierrc --ignore-path ../../.gitignore --ignore-path .gitignore --ignore-path ../../.prettierignore" + }, + "files": [ + "dist" + ], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "dependencies": { + "@sveltejs/cli-core": "workspace:*", + "playwright": "^1.44.1", + "sv": "workspace:*", + "terminate": "^2.8.0", + "tiged": "3.0.0-rc.0" + } +} diff --git a/packages/adder-testing-library/tests.ts b/packages/adder-testing-library/tests.ts new file mode 100644 index 00000000..4561d4b9 --- /dev/null +++ b/packages/adder-testing-library/tests.ts @@ -0,0 +1,98 @@ +import type { OptionValues, Question, Tests } from '@sveltejs/cli-core'; +import type { Page } from 'playwright'; +import type { AdderWithTests } from './index.ts'; + +export async function startTests( + page: Page, + adder: AdderWithTests, + options: OptionValues> +): Promise { + const tests: Tests = { + expectProperty: async (selector, property, expectedValue) => { + await expectProperty(page, selector, property, expectedValue); + }, + elementExists: async (selector) => { + await elementExists(page, selector); + }, + click: async (selector, path) => { + await click(page, selector, path); + }, + expectUrlPath: (path) => { + expectUrlPath(page, path); + } + }; + + await executeAdderTests(adder, tests, options); +} + +async function executeAdderTests( + adder: AdderWithTests, + testMethods: Tests, + options: OptionValues> +) { + if (!adder.tests || adder.tests.tests.length == 0) + throw new Error('Cannot test adder without tests!'); + + for (const test of adder.tests.tests) { + if (test.condition && !test.condition(options)) continue; + + await test.run(testMethods); + } +} + +async function elementExists(page: Page, selector: string) { + const elementToCheck = await page.$(selector); + if (!elementToCheck) { + throw new Error('No element found for selector ' + selector); + } + + return elementToCheck; +} + +/** + * @param path If the click action results in a navigation, provide the expected path + * + * @example + * ```js + * await click(page, "a.some-link", "/some-path"); + * ``` + */ +async function click(page: Page, selector: string, path?: string) { + await elementExists(page, selector); + + await page.click(selector); + + if (path) { + await page.waitForURL((url) => url.pathname === path); + } +} + +function expectUrlPath(page: Page, path: string) { + const url = new URL(page.url()); + + if (url.pathname !== path) { + throw new Error(`Found path ${url.pathname} but expected ${path}!`); + } +} + +async function expectProperty( + page: Page, + selector: string, + property: string, + expectedValue: string +) { + const elementToCheck = await elementExists(page, selector); + + const computedStyle = await page.evaluate( + ([element, pV]) => window.getComputedStyle(element).getPropertyValue(pV), + [elementToCheck, property] as const + ); + + if (computedStyle !== expectedValue) { + throw new Error( + `Expected '${expectedValue}' but got '${computedStyle}' for selector '${selector}'` + ); + } + + return computedStyle; +} diff --git a/packages/adder-testing-library/tsconfig.json b/packages/adder-testing-library/tsconfig.json new file mode 100644 index 00000000..a1fcf6c9 --- /dev/null +++ b/packages/adder-testing-library/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "checkJs": false, + "isolatedDeclarations": true, + "declaration": true + } +} diff --git a/packages/adder-testing-library/utils.ts b/packages/adder-testing-library/utils.ts new file mode 100644 index 00000000..fd92a4b8 --- /dev/null +++ b/packages/adder-testing-library/utils.ts @@ -0,0 +1,311 @@ +import { execSync, spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'; +import path from 'node:path'; +import fs from 'node:fs'; +import tiged from 'tiged'; +import terminate from 'terminate'; +import { create, createOrUpdateFiles, createWorkspace, installPackages } from 'sv'; +import { type OptionValues, type Question } from '@sveltejs/cli-core'; +import { startBrowser } from './browser.ts'; +import type { AdderWithTests } from './index.ts'; + +export type TestCase = { + testName: string; + template: string; + adder: AdderWithTests; + options: OptionValues>; + cwd: string; +}; + +export const ProjectTypes = { + Svelte_JS: 'svelte-js', + Svelte_TS: 'svelte-ts', + Kit_JS: 'kit-js', + Kit_JS_Comments: 'kit-js-comments', + Kit_TS: 'kit-ts' +}; +export const ProjectTypesList: string[] = Object.values(ProjectTypes); + +export async function forceKill(devServer: ChildProcessWithoutNullStreams): Promise { + return new Promise((resolve) => { + if (!devServer.pid) return; + + // just killing the process was not enough, because the process itself + // spawns child process, that also need to be killed! + terminate(devServer.pid, () => { + resolve(); + }); + }); +} + +export async function downloadProjectTemplates(outputPath: string): Promise { + for (const templateType of ProjectTypesList) { + const templateOutputPath = path.join(outputPath, templateType); + + if (templateType.includes('kit')) { + create(templateOutputPath, { + name: templateType, + template: 'minimal', + types: + templateType == ProjectTypes.Kit_TS + ? 'typescript' + : templateType == ProjectTypes.Kit_JS_Comments + ? 'checkjs' + : 'none' + }); + } else { + const templateName = + templateType == ProjectTypes.Svelte_TS ? 'template-svelte-ts' : 'template-svelte'; + + const emitter = tiged(`vitejs/vite/packages/create-vite/${templateName}`, { + cache: false, + force: true, + verbose: false + }); + + await emitter.clone(templateOutputPath); + } + } +} + +export async function startDevServer( + output: string, + command: string +): Promise<{ url: string; devServer: ChildProcessWithoutNullStreams }> { + try { + const program = spawn('pnpm', ['run', command], { stdio: 'pipe', shell: true, cwd: output }); + + return await new Promise((resolve) => { + program.stdout?.on('data', (data: Buffer) => { + const value = data.toString(); + + // extract dev server url from console output + const regexUnicode = /[^\x20-\xaf]+/g; + const withoutUnicode = value.replace(regexUnicode, ''); + + const regexUnicodeDigits = /\[[0-9]{1,2}m/g; + const withoutColors = withoutUnicode.replace(regexUnicodeDigits, ''); + + const regexUrl = /http:\/\/[^:\s]+:[0-9]+\//g; + const urls = withoutColors.match(regexUrl); + + if (urls && urls.length > 0) { + const url = urls[0]; + resolve({ url, devServer: program }); + } + }); + }); + } catch (error) { + const typedError = error as Error; + throw new Error('Failed to start dev server' + typedError.message); + } +} + +export async function stopDevServer(devServer: ChildProcessWithoutNullStreams): Promise { + if (!devServer.pid) return; + + await forceKill(devServer); +} + +export function generateTestCases( + adders: AdderWithTests[], + addersOutputPath: string, + options: { ignoreEmptyTests: boolean } +): Map { + const testCases = new Map(); + for (const adder of adders) { + const adderId = adder.config.id; + const adderTestCases: TestCase[] = []; + const testData = adder.tests; + if (!testData || !testData.tests || (options.ignoreEmptyTests && testData.tests.length == 0)) + continue; + + for (const template of ProjectTypesList) { + const environments = adder.config.environments; + if ( + (!environments.kit && template.includes('kit')) || + (!environments.svelte && template.includes('svelte')) + ) { + continue; + } + + const optionsCombinations = testData.optionValues; + // if list if empty, add empty options so that one testcase gets run + if (optionsCombinations.length == 0) optionsCombinations.push({}); + + for (const options of optionsCombinations) { + let optionDirectoryName = Object.entries(options) + .map(([key, value]) => `${key}=${value}`) + .join('+'); + if (!optionDirectoryName) optionDirectoryName = 'default'; + const cwd = path.join(addersOutputPath, adderId, template, optionDirectoryName); + const testName = `${adder.config.id} / ${template} / ${JSON.stringify(options)}`; + + const testCase: TestCase = { + testName, + adder, + options, + template, + cwd + }; + + adderTestCases.push(testCase); + } + } + + testCases.set(adderId, adderTestCases); + } + return testCases; +} + +export async function prepareEndToEndTests( + outputPath: string, + templatesPath: string, + addersPath: string, + adders: AdderWithTests[], + testCases: Map +): Promise { + console.log('deleting old files'); + // only delete adders and templates directory. Trying to delete `node_modules` + // typically fails because some `esbuild` binary is locked + fs.rmSync(addersPath, { recursive: true, force: true }); + fs.rmSync(templatesPath, { recursive: true, force: true }); + + fs.mkdirSync(outputPath, { recursive: true }); + + console.log('downloading project templates'); + await downloadProjectTemplates(templatesPath); + + const dirs: string[] = []; + for (const type of Object.values(ProjectTypes)) { + dirs.push(...adders.map((a) => ` - 'adders/${a.config.id}/${type}/*'`)); + } + + const pnpmWorkspace = `packages:\n${dirs.join('\n')}\n`; + fs.writeFileSync(path.join(outputPath, 'pnpm-workspace.yaml'), pnpmWorkspace, { + encoding: 'utf8' + }); + + const testRootPkgJson = JSON.stringify({ name: 'test-root', version: '0.0.0', type: 'module' }); + fs.writeFileSync(path.join(outputPath, 'package.json'), testRootPkgJson, { + encoding: 'utf8' + }); + + console.log('executing adders'); + for (const adderTestCases of testCases.values()) { + const applyAdderTasks = []; + for (const testCase of adderTestCases) { + fs.mkdirSync(testCase.cwd, { recursive: true }); + + // copy template into working directory + const templatePath = path.join(templatesPath, testCase.template); + fs.cpSync(templatePath, testCase.cwd, { recursive: true }); + + applyAdderTasks.push(runAdder(testCase.adder, testCase.cwd, testCase.options, adders)); + } + + await Promise.all(applyAdderTasks); + } + + console.log('preparing test files'); + for (const adderTestCases of testCases.values()) { + for (const testCase of adderTestCases) { + const workspace = createWorkspace({ cwd: testCase.cwd }); + workspace.options = testCase.options; + createOrUpdateFiles(testCase.adder.tests?.files ?? [], workspace); + } + } + + console.log('installing dependencies'); + execSync('pnpm install', { cwd: outputPath, stdio: 'pipe' }); + + await startBrowser(); + + console.log('start testing'); +} + +export async function prepareSnapshotTests( + outputPath: string, + templatesPath: string, + addersPath: string, + testCases: Map +): Promise { + console.log('deleting old files'); + // only delete adders and templates directory. Trying to delete `node_modules` + // typically fails because some `esbuild` binary is locked + fs.rmSync(addersPath, { recursive: true, force: true }); + fs.rmSync(templatesPath, { recursive: true, force: true }); + + fs.mkdirSync(outputPath, { recursive: true }); + + console.log('downloading project templates'); + await downloadProjectTemplates(templatesPath); + + console.log('preparing adder templates'); + // create all relevant directories with the templates + for (const adderTestCases of testCases.values()) { + for (const testCase of adderTestCases) { + fs.mkdirSync(testCase.cwd, { recursive: true }); + + // copy template into working directory + const templatePath = path.join(templatesPath, testCase.template); + fs.cpSync(templatePath, testCase.cwd, { recursive: true }); + } + } +} + +export function runAdder( + adder: AdderWithTests, + cwd: string, + options: OptionValues>, + adders: AdderWithTests[] +): Set { + const { config } = adder; + const workspace = createWorkspace({ cwd }); + + workspace.options = options; + + const filesToFormat = new Set(); + + // execute adders + if (config.dependsOn) { + for (const dependencyAdderId of config.dependsOn) { + const dependencyAdder = adders.find((x) => x.config.id == dependencyAdderId); + + if (!dependencyAdder) + throw new Error( + `failed to find required dependency '${dependencyAdderId}' of adder ${adder.config.id}` + ); + + // apply default adder options + const options: Record = {}; + for (const [key, question] of Object.entries(dependencyAdder.config.options)) { + options[key] = question.default; + } + + runAdder(dependencyAdder, cwd, options as OptionValues>, adders); + } + } + + const pkgPath = installPackages(config, workspace); + filesToFormat.add(pkgPath); + const changedFiles = createOrUpdateFiles(config.files, workspace); + changedFiles.forEach((file) => filesToFormat.add(file)); + + if (config.scripts && config.scripts.length > 0) { + for (const script of config.scripts) { + if (script.condition?.(workspace) === false) continue; + + try { + execSync('pnpx ' + script.args.join(' '), { + cwd: workspace.cwd, + stdio: 'pipe' + }); + } catch (error) { + const typedError = error as Error; + throw new Error(`Failed to execute scripts '${script.description}': ` + typedError.message); + } + } + } + + return filesToFormat; +} diff --git a/packages/adder-tests/common.ts b/packages/adder-tests/common.ts new file mode 100644 index 00000000..0e1cbb94 --- /dev/null +++ b/packages/adder-tests/common.ts @@ -0,0 +1,12 @@ +import { getAdderDetails, officialAdders } from '@sveltejs/adders'; + +export async function getAdderTestDetails() { + return Promise.all( + officialAdders.map(async (x) => { + return { + config: getAdderDetails(x.id), + tests: (await import(`../adders/${x.id}/tests.ts`)).tests + }; + }) + ); +} diff --git a/packages/adder-tests/package.json b/packages/adder-tests/package.json new file mode 100644 index 00000000..33378254 --- /dev/null +++ b/packages/adder-tests/package.json @@ -0,0 +1,17 @@ +{ + "name": "@sveltejs/adder-tests", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "check": "tsc", + "format": "pnpm lint --write", + "lint": "prettier --check . --config ../../.prettierrc --ignore-path ../../.gitignore --ignore-path .gitignore --ignore-path ../../.prettierignore", + "test": "vitest run", + "test:ui": "vitest --ui" + }, + "dependencies": { + "@sveltejs/adder-testing-library": "workspace:*", + "@sveltejs/adders": "workspace:*" + } +} diff --git a/packages/adder-tests/tests/official/end2end.ts b/packages/adder-tests/tests/official/end2end.ts new file mode 100644 index 00000000..f780235d --- /dev/null +++ b/packages/adder-tests/tests/official/end2end.ts @@ -0,0 +1,7 @@ +import { test, describe, beforeAll, afterAll } from 'vitest'; +import { getAdderTestDetails } from '../../common.ts'; +import { runEndToEndTests } from '@sveltejs/adder-testing-library'; + +const adders = await getAdderTestDetails(); + +runEndToEndTests('.outputs-e2e', adders, describe, test.concurrent, beforeAll, afterAll); diff --git a/packages/adder-tests/tests/official/snapshots.ts b/packages/adder-tests/tests/official/snapshots.ts new file mode 100644 index 00000000..bf98d8b6 --- /dev/null +++ b/packages/adder-tests/tests/official/snapshots.ts @@ -0,0 +1,15 @@ +import { test, describe, beforeAll, afterAll } from 'vitest'; +import { runSnapshotTests } from '@sveltejs/adder-testing-library'; +import { getAdderTestDetails } from '../../common.ts'; + +const adders = await getAdderTestDetails(); + +runSnapshotTests( + '.outputs-snapshots', + '_snapshots', + adders, + describe, + test.concurrent, + beforeAll, + afterAll +); diff --git a/packages/adder-tests/vitest.config.ts b/packages/adder-tests/vitest.config.ts new file mode 100644 index 00000000..facb0b3d --- /dev/null +++ b/packages/adder-tests/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig, type UserConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['./tests/**/*.ts'], + testTimeout: 1000 * 60 * 2, // 2 minutes + hookTimeout: 1000 * 60 * 3, // 3 minutes + pool: 'threads' + } +}) as UserConfig; diff --git a/packages/adders/_config/index.ts b/packages/adders/_config/index.ts index 80b647a8..fd4d3c16 100644 --- a/packages/adders/_config/index.ts +++ b/packages/adders/_config/index.ts @@ -1,2 +1,2 @@ -export { officialAdders, getAdderDetails } from './official.ts'; +export { officialAdders, getAdderDetails, getAdderTestDetails } from './official.ts'; export { getCommunityAdder, communityAdderIds } from './community.ts'; diff --git a/packages/adders/drizzle/docker-compose.yml b/packages/adders/drizzle/docker-compose.yml new file mode 100644 index 00000000..f6f5e0a7 --- /dev/null +++ b/packages/adders/drizzle/docker-compose.yml @@ -0,0 +1,20 @@ +services: + db-postgres: + image: postgres + restart: always + shm_size: 128mb + ports: + - 5432:5432 + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: mysecretpassword + POSTGRES_DB: local + db-mysql: + image: mysql + restart: always + shm_size: 128mb + ports: + - 3306:3306 + environment: + MYSQL_ROOT_PASSWORD: mysecretpassword + MYSQL_DATABASE: local diff --git a/packages/adders/drizzle/index.ts b/packages/adders/drizzle/index.ts index f3174054..5e48fb26 100644 --- a/packages/adders/drizzle/index.ts +++ b/packages/adders/drizzle/index.ts @@ -76,45 +76,49 @@ export default defineAdder({ name: 'mysql2', version: '^3.11.0', dev: false, - condition: ({ options }) => options.mysql === 'mysql2' + condition: ({ options }) => options.database == 'mysql' && options.mysql === 'mysql2' }, { name: '@planetscale/database', version: '^1.18.0', dev: false, - condition: ({ options }) => options.mysql === 'planetscale' + condition: ({ options }) => options.database == 'mysql' && options.mysql === 'planetscale' }, // PostgreSQL { name: '@neondatabase/serverless', version: '^0.9.4', dev: false, - condition: ({ options }) => options.postgresql === 'neon' + condition: ({ options }) => options.database == 'postgresql' && options.postgresql === 'neon' }, { name: 'postgres', version: '^3.4.4', dev: false, - condition: ({ options }) => options.postgresql === 'postgres.js' + condition: ({ options }) => + options.database == 'postgresql' && options.postgresql === 'postgres.js' }, // SQLite { name: 'better-sqlite3', version: '^11.1.2', dev: false, - condition: ({ options }) => options.sqlite === 'better-sqlite3' + condition: ({ options }) => + options.database == 'sqlite' && options.sqlite === 'better-sqlite3' }, { name: '@types/better-sqlite3', version: '^7.6.11', dev: true, - condition: ({ options }) => options.sqlite === 'better-sqlite3' + condition: ({ options }) => + options.database == 'sqlite' && options.sqlite === 'better-sqlite3' }, { name: '@libsql/client', version: '^0.9.0', dev: false, - condition: ({ options }) => options.sqlite === 'libsql' || options.sqlite === 'turso' + condition: ({ options }) => + options.database == 'sqlite' && (options.sqlite === 'libsql' || options.sqlite === 'turso') } ], files: [ @@ -129,7 +133,9 @@ export default defineAdder({ { name: () => 'docker-compose.yml', condition: ({ options }) => - options.docker && (options.mysql === 'mysql2' || options.postgresql === 'postgres.js'), + options.docker && + ((options.database == 'mysql' && options.mysql === 'mysql2') || + (options.database == 'postgresql' && options.postgresql === 'postgres.js')), content: ({ content, options }) => { // if the file already exists, don't modify it // (in the future, we could add some tooling for modifying yaml) @@ -143,13 +149,13 @@ export default defineAdder({ const DB_NAME = 'local'; let dbSpecificContent = ''; - if (options.mysql === 'mysql2') { + if (options.database == 'mysql' && options.mysql === 'mysql2') { dbSpecificContent = ` MYSQL_ROOT_PASSWORD: ${PASSWORD} MYSQL_DATABASE: ${DB_NAME} `; } - if (options.postgresql === 'postgres.js') { + if (options.database == 'postgresql' && options.postgresql === 'postgres.js') { dbSpecificContent = ` POSTGRES_USER: ${USER} POSTGRES_PASSWORD: ${PASSWORD} @@ -215,7 +221,10 @@ export default defineAdder({ const objExpression = exportDefault.arguments?.[0]; if (!objExpression || objExpression.type !== 'ObjectExpression') return content; - const driver = options.sqlite === 'turso' ? common.createLiteral('turso') : undefined; + const driver = + options.database == 'sqlite' && options.sqlite === 'turso' + ? common.createLiteral('turso') + : undefined; const authToken = options.sqlite === 'turso' ? common.expressionFromString('process.env.DATABASE_AUTH_TOKEN') @@ -312,13 +321,16 @@ export default defineAdder({ let clientExpression; // SQLite - if (options.sqlite === 'better-sqlite3') { + if (options.database == 'sqlite' && options.sqlite === 'better-sqlite3') { imports.addDefault(ast, 'better-sqlite3', 'Database'); imports.addNamed(ast, 'drizzle-orm/better-sqlite3', { drizzle: 'drizzle' }); clientExpression = common.expressionFromString('new Database(env.DATABASE_URL)'); } - if (options.sqlite === 'libsql' || options.sqlite === 'turso') { + if ( + options.database == 'sqlite' && + (options.sqlite === 'libsql' || options.sqlite === 'turso') + ) { imports.addNamed(ast, '@libsql/client', { createClient: 'createClient' }); imports.addNamed(ast, 'drizzle-orm/libsql', { drizzle: 'drizzle' }); @@ -340,7 +352,7 @@ export default defineAdder({ } } // MySQL - if (options.mysql === 'mysql2') { + if (options.database == 'mysql' && options.mysql === 'mysql2') { imports.addDefault(ast, 'mysql2/promise', 'mysql'); imports.addNamed(ast, 'drizzle-orm/mysql2', { drizzle: 'drizzle' }); @@ -348,20 +360,20 @@ export default defineAdder({ 'await mysql.createConnection(env.DATABASE_URL)' ); } - if (options.mysql === 'planetscale') { + if (options.database == 'mysql' && options.mysql === 'planetscale') { imports.addNamed(ast, '@planetscale/database', { Client: 'Client' }); imports.addNamed(ast, 'drizzle-orm/planetscale-serverless', { drizzle: 'drizzle' }); clientExpression = common.expressionFromString('new Client({ url: env.DATABASE_URL })'); } // PostgreSQL - if (options.postgresql === 'neon') { + if (options.database == 'postgresql' && options.postgresql === 'neon') { imports.addNamed(ast, '@neondatabase/serverless', { neon: 'neon' }); imports.addNamed(ast, 'drizzle-orm/neon-http', { drizzle: 'drizzle' }); clientExpression = common.expressionFromString('neon(env.DATABASE_URL)'); } - if (options.postgresql === 'postgres.js') { + if (options.database == 'postgresql' && options.postgresql === 'postgres.js') { imports.addDefault(ast, 'postgres', 'postgres'); imports.addNamed(ast, 'drizzle-orm/postgres-js', { drizzle: 'drizzle' }); diff --git a/packages/adders/drizzle/tests.ts b/packages/adders/drizzle/tests.ts index 3257b575..8dcabb76 100644 --- a/packages/adders/drizzle/tests.ts +++ b/packages/adders/drizzle/tests.ts @@ -1,6 +1,9 @@ -import { options } from './index.ts'; import { defineAdderTests } from '@sveltejs/cli-core'; -import { parseSvelte, parseJson } from '@sveltejs/cli-core/parsers'; +import path from 'node:path'; +import url from 'node:url'; +import { execSync } from 'node:child_process'; +import { options } from './index.ts'; +import { parseSvelte } from '@sveltejs/cli-core/parsers'; const defaultOptionValues = { sqlite: options.sqlite.default, @@ -9,6 +12,8 @@ const defaultOptionValues = { docker: options.docker.default }; +const dockerComposeCwd = path.resolve(url.fileURLToPath(import.meta.url), '..'); + export const tests = defineAdderTests({ options, optionValues: [ @@ -64,17 +69,31 @@ export const tests = defineAdderTests({ content: ({ content }) => { return content.replace('strict: true,', ''); } - }, - { - name: () => 'package.json', - content: ({ content }) => { - const { data, generateCode } = parseJson(content); - // executes after pnpm install - data.scripts['postinstall'] ??= 'pnpm run db:push'; - return generateCode(); - } } ], + beforeAll: async (testType) => { + if (testType == 'snapshot') return; + + console.log('Starting docker containers'); + execSync('docker compose up --detach', { cwd: dockerComposeCwd, stdio: 'pipe' }); + + // the containers take some time to startup and be ready + // to accept any connections. As there is no standard / easy + // way of doing this for different containers (mysql / postgres) + // we are waiting for them to startup + await new Promise((x) => setTimeout(x, 15000)); + }, + afterAll: (testType) => { + if (testType == 'snapshot') return; + + console.log('Stopping docker containers'); + execSync('docker compose down --volumes', { cwd: dockerComposeCwd, stdio: 'pipe' }); + }, + beforeEach: (cwd, testType) => { + if (testType == 'snapshot') return; + + execSync('pnpm db:push', { cwd, stdio: 'pipe' }); + }, tests: [ { name: 'queries database', diff --git a/packages/cli/env.ts b/packages/cli/env.ts index d482918b..4ab46826 100644 --- a/packages/cli/env.ts +++ b/packages/cli/env.ts @@ -1,3 +1,3 @@ import process from 'node:process'; -export const TESTING: boolean = process.env.CI?.toLowerCase() === 'true'; +export const TESTING: boolean = process.env.NODE_ENV?.toLowerCase() === 'test'; diff --git a/packages/cli/index.ts b/packages/cli/index.ts index 50f83ec1..4fb7e779 100644 --- a/packages/cli/index.ts +++ b/packages/cli/index.ts @@ -1,3 +1,10 @@ import { create } from '@sveltejs/create'; +import { createWorkspace } from './commands/add/workspace.ts'; +import { installPackages } from './commands/add/utils.ts'; +import { createOrUpdateFiles } from './commands/add/processor.ts'; export { create }; + +// todo: this should not be exported here, rather in something internal +// we should avoid duplicating the code thought due to maintance overhead +export { createWorkspace, installPackages, createOrUpdateFiles }; diff --git a/packages/core/adder/config.ts b/packages/core/adder/config.ts index f19cb146..8bd3bcc6 100644 --- a/packages/core/adder/config.ts +++ b/packages/core/adder/config.ts @@ -72,13 +72,18 @@ export type TestDefinition = { condition?: (options: OptionValues) => boolean; }; +export type TestType = 'snapshot' | 'end2end'; + export type AdderTestConfig = { files: Array>; options: Args; optionValues: Array>; - runSynchronously?: boolean; command?: string; tests: Array>; + beforeAll?: (testType: TestType) => MaybePromise; + afterAll?: (testType: TestType) => MaybePromise; + beforeEach?: (cwd: string, testType: TestType) => MaybePromise; + afterEach?: (cwd: string, testType: TestType) => MaybePromise; }; export function defineAdderTests( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 31227042..42da4297 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,8 +36,8 @@ importers: specifier: ^22.3.0 version: 22.5.4 '@vitest/ui': - specifier: ^2.0.5 - version: 2.0.5(vitest@2.0.5) + specifier: ^2.1.2 + version: 2.1.2(vitest@2.1.2) eslint: specifier: ^9.10.0 version: 9.10.0 @@ -78,8 +78,8 @@ importers: specifier: ^0.6.5 version: 0.6.5(rollup@4.21.2)(typescript@5.6.2)(webpack-sources@3.2.3) vitest: - specifier: ^2.0.5 - version: 2.0.5(@types/node@22.5.4)(@vitest/ui@2.0.5) + specifier: ^2.1.2 + version: 2.1.2(@types/node@22.5.4)(@vitest/ui@2.1.2) community-adder-template: dependencies: @@ -87,9 +87,42 @@ importers: specifier: workspace:* version: link:../packages/core devDependencies: + '@sveltejs/adder-testing-library': + specifier: workspace:* + version: link:../packages/adder-testing-library sv: specifier: workspace:* version: link:../packages/cli + vitest: + specifier: ^2.1.2 + version: 2.1.2(@types/node@22.5.4)(@vitest/ui@2.1.2) + + packages/adder-testing-library: + dependencies: + '@sveltejs/cli-core': + specifier: workspace:* + version: link:../core + playwright: + specifier: ^1.44.1 + version: 1.47.2 + sv: + specifier: workspace:* + version: link:../cli + terminate: + specifier: ^2.8.0 + version: 2.8.0 + tiged: + specifier: 3.0.0-rc.0 + version: 3.0.0-rc.0 + + packages/adder-tests: + dependencies: + '@sveltejs/adder-testing-library': + specifier: workspace:* + version: link:../adder-testing-library + '@sveltejs/adders': + specifier: workspace:* + version: link:../adders packages/adders: dependencies: @@ -498,6 +531,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@jridgewell/gen-mapping@0.3.5': resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} @@ -809,28 +846,40 @@ packages: resolution: {integrity: sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitest/expect@2.0.5': - resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} + '@vitest/expect@2.1.2': + resolution: {integrity: sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==} + + '@vitest/mocker@2.1.2': + resolution: {integrity: sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==} + peerDependencies: + '@vitest/spy': 2.1.2 + msw: ^2.3.5 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true - '@vitest/pretty-format@2.0.5': - resolution: {integrity: sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==} + '@vitest/pretty-format@2.1.2': + resolution: {integrity: sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==} - '@vitest/runner@2.0.5': - resolution: {integrity: sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==} + '@vitest/runner@2.1.2': + resolution: {integrity: sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==} - '@vitest/snapshot@2.0.5': - resolution: {integrity: sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==} + '@vitest/snapshot@2.1.2': + resolution: {integrity: sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==} - '@vitest/spy@2.0.5': - resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} + '@vitest/spy@2.1.2': + resolution: {integrity: sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==} - '@vitest/ui@2.0.5': - resolution: {integrity: sha512-m+ZpVt/PVi/nbeRKEjdiYeoh0aOfI9zr3Ria9LO7V2PlMETtAXJS3uETEZkc8Be2oOl8mhd7Ew+5SRBXRYncNw==} + '@vitest/ui@2.1.2': + resolution: {integrity: sha512-92gcNzkDnmxOxyHzQrQYRsoV9Q0Aay0r4QMLnV+B+lbqlUWa8nDg9ivyLV5mMVTtGirHsYUGGh/zbIA55gBZqA==} peerDependencies: - vitest: 2.0.5 + vitest: 2.1.2 - '@vitest/utils@2.0.5': - resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} + '@vitest/utils@2.1.2': + resolution: {integrity: sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -847,6 +896,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + engines: {node: '>= 14'} + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -969,6 +1022,10 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} @@ -980,6 +1037,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + commander@12.1.0: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} @@ -1074,6 +1134,9 @@ packages: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -1215,9 +1278,8 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} + event-stream@3.3.4: + resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} @@ -1245,6 +1307,14 @@ packages: fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fdir@6.3.0: + resolution: {integrity: sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} @@ -1275,6 +1345,13 @@ packages: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} + from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -1283,6 +1360,11 @@ packages: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1291,6 +1373,9 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + fuzzysearch@1.0.3: + resolution: {integrity: sha512-s+kNWQuI3mo9OALw0HJ6YGmMbLqEufCh2nX/zzV5CrICQ/y4AwPxM+6TIiF9ItFCHXFCyM/BfCCmN57NTIJuPg==} + get-func-name@2.0.2: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} @@ -1298,10 +1383,6 @@ packages: resolution: {integrity: sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==} engines: {node: '>=12'} - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - get-tsconfig@4.8.0: resolution: {integrity: sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==} @@ -1363,13 +1444,13 @@ packages: htmlparser2@9.1.0: resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + https-proxy-agent@7.0.4: + resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==} + engines: {node: '>= 14'} + human-id@1.0.2: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -1427,10 +1508,6 @@ packages: is-reference@3.0.2: resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-subdir@1.2.0: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} @@ -1469,6 +1546,9 @@ packages: jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1518,8 +1598,8 @@ packages: magic-string@0.30.12: resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==} - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + map-stream@0.1.0: + resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} @@ -1529,10 +1609,6 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -1544,6 +1620,15 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + minizlib@3.0.1: + resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==} + engines: {node: '>= 18'} + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -1575,10 +1660,6 @@ packages: encoding: optional: true - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -1586,10 +1667,6 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -1650,10 +1727,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -1672,6 +1745,9 @@ packages: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} + pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + picocolors@1.1.0: resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} @@ -1679,6 +1755,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} @@ -1687,6 +1767,16 @@ packages: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} + playwright-core@1.47.2: + resolution: {integrity: sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.47.2: + resolution: {integrity: sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==} + engines: {node: '>=18'} + hasBin: true + postcss-load-config@3.1.4: resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} engines: {node: '>= 10'} @@ -1747,6 +1837,11 @@ packages: engines: {node: '>=14'} hasBin: true + ps-tree@1.2.0: + resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} + engines: {node: '>= 0.10'} + hasBin: true + pseudomap@1.0.2: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} @@ -1793,6 +1888,11 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rimraf@5.0.7: + resolution: {integrity: sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==} + engines: {node: '>=14.18'} + hasBin: true + rollup-plugin-esbuild@6.1.1: resolution: {integrity: sha512-CehMY9FAqJD5OUaE/Mi1r5z0kNeYxItmRO2zG4Qnv2qWKF09J2lTy5GUzjJR354ZPrLkCj4fiBN41lo8PzBUhw==} engines: {node: '>=14.18.0'} @@ -1887,6 +1987,9 @@ packages: spawndamnit@2.0.0: resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + split@0.3.3: + resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -1896,6 +1999,9 @@ packages: std-env@3.7.0: resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + stream-combiner@0.0.4: + resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + streamx@2.20.1: resolution: {integrity: sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==} @@ -1919,10 +2025,6 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -1967,10 +2069,18 @@ packages: tar-stream@3.1.7: resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + tar@7.4.0: + resolution: {integrity: sha512-XQs0S8fuAkQWuqhDeCdMlJXDX80D7EOVLDPVFkna9yQfzS+PHKgfxcei0jf6/+QAWcjqrnC8uM3fSAnrQl+XYg==} + engines: {node: '>=18'} + term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} + terminate@2.8.0: + resolution: {integrity: sha512-bcbjJEg0wY5nuJXvGxxHfmoEPkyHLCctUKO6suwtxy7jVSgGcgPeGwpbLDLELFhIaxCGRr3dPvyNg1yuz2V0eg==} + engines: {node: '>=12'} + text-decoder@1.2.0: resolution: {integrity: sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==} @@ -1984,6 +2094,14 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tiged@3.0.0-rc.0: + resolution: {integrity: sha512-BfPQmGvFoh0DaTl45/I+kxo0q8kAy+HgZgpSMn5gJdYVns5pARkvwW/Er2dldDiue+uwnMvSoIPfQBbphVszCw==} + engines: {node: '>=8.0.0'} + hasBin: true + tiny-glob@0.2.9: resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} @@ -1996,6 +2114,10 @@ packages: tinyexec@0.3.0: resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} + tinyglobby@0.2.6: + resolution: {integrity: sha512-NbBoFBpqfcgd1tCiO8Lkfdk+xrA7mlLR9zgvZcZWQQwU63XAfUePyd6wZBaU93Hqw347lHnwFzttAkemHzzz4g==} + engines: {node: '>=12.0.0'} + tinypool@1.0.1: resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -2064,6 +2186,10 @@ packages: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + unplugin-isolated-decl@0.6.5: resolution: {integrity: sha512-bEtUFVSTczPKAXrQiRQRfi55ziDt143G5ZL/jpj2+NoOJIxT5rjWltwHIlHXS6ym/LyuoqoZApTHkmMTFn6YTA==} engines: {node: '>=18.12.0'} @@ -2102,8 +2228,8 @@ packages: typescript: optional: true - vite-node@2.0.5: - resolution: {integrity: sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==} + vite-node@2.1.2: + resolution: {integrity: sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -2138,15 +2264,15 @@ packages: terser: optional: true - vitest@2.0.5: - resolution: {integrity: sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==} + vitest@2.1.2: + resolution: {integrity: sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.0.5 - '@vitest/ui': 2.0.5 + '@vitest/browser': 2.1.2 + '@vitest/ui': 2.1.2 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -2208,6 +2334,10 @@ packages: yallist@2.1.2: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} @@ -2512,6 +2642,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 @@ -2808,47 +2942,54 @@ snapshots: '@typescript-eslint/types': 8.5.0 eslint-visitor-keys: 3.4.3 - '@vitest/expect@2.0.5': + '@vitest/expect@2.1.2': dependencies: - '@vitest/spy': 2.0.5 - '@vitest/utils': 2.0.5 + '@vitest/spy': 2.1.2 + '@vitest/utils': 2.1.2 chai: 5.1.1 tinyrainbow: 1.2.0 - '@vitest/pretty-format@2.0.5': + '@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(vite@5.4.3(@types/node@22.5.4))': + dependencies: + '@vitest/spy': 2.1.2 + estree-walker: 3.0.3 + magic-string: 0.30.12 + optionalDependencies: + vite: 5.4.3(@types/node@22.5.4) + + '@vitest/pretty-format@2.1.2': dependencies: tinyrainbow: 1.2.0 - '@vitest/runner@2.0.5': + '@vitest/runner@2.1.2': dependencies: - '@vitest/utils': 2.0.5 + '@vitest/utils': 2.1.2 pathe: 1.1.2 - '@vitest/snapshot@2.0.5': + '@vitest/snapshot@2.1.2': dependencies: - '@vitest/pretty-format': 2.0.5 + '@vitest/pretty-format': 2.1.2 magic-string: 0.30.12 pathe: 1.1.2 - '@vitest/spy@2.0.5': + '@vitest/spy@2.1.2': dependencies: tinyspy: 3.0.2 - '@vitest/ui@2.0.5(vitest@2.0.5)': + '@vitest/ui@2.1.2(vitest@2.1.2)': dependencies: - '@vitest/utils': 2.0.5 - fast-glob: 3.3.2 + '@vitest/utils': 2.1.2 fflate: 0.8.2 flatted: 3.3.1 pathe: 1.1.2 sirv: 2.0.4 + tinyglobby: 0.2.6 tinyrainbow: 1.2.0 - vitest: 2.0.5(@types/node@22.5.4)(@vitest/ui@2.0.5) + vitest: 2.1.2(@types/node@22.5.4)(@vitest/ui@2.1.2) - '@vitest/utils@2.0.5': + '@vitest/utils@2.1.2': dependencies: - '@vitest/pretty-format': 2.0.5 - estree-walker: 3.0.3 + '@vitest/pretty-format': 2.1.2 loupe: 3.1.1 tinyrainbow: 1.2.0 @@ -2862,6 +3003,12 @@ snapshots: acorn@8.12.1: {} + agent-base@7.1.1: + dependencies: + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -2975,6 +3122,8 @@ snapshots: check-error@2.1.1: {} + chownr@3.0.0: {} + ci-info@3.9.0: {} color-convert@2.0.1: @@ -2983,6 +3132,8 @@ snapshots: color-name@1.1.4: {} + colorette@2.0.20: {} + commander@12.1.0: {} commander@4.1.1: {} @@ -3051,6 +3202,8 @@ snapshots: dotenv@16.4.5: {} + duplexer@0.1.2: {} + eastasianwidth@0.2.0: {} emoji-regex@8.0.0: {} @@ -3244,17 +3397,15 @@ snapshots: esutils@2.0.3: {} - execa@8.0.1: + event-stream@3.3.4: dependencies: - cross-spawn: 7.0.3 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 + duplexer: 0.1.2 + from: 0.1.7 + map-stream: 0.1.0 + pause-stream: 0.0.11 + split: 0.3.3 + stream-combiner: 0.0.4 + through: 2.3.8 extendable-error@0.1.7: {} @@ -3284,6 +3435,10 @@ snapshots: dependencies: reusify: 1.0.4 + fdir@6.3.0(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + fflate@0.8.2: {} file-entry-cache@8.0.0: @@ -3316,6 +3471,14 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 + from@0.1.7: {} + + fs-extra@11.2.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 @@ -3328,17 +3491,20 @@ snapshots: jsonfile: 4.0.0 universalify: 0.1.2 + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true function-bind@1.1.2: {} + fuzzysearch@1.0.3: {} + get-func-name@2.0.2: {} get-stdin@9.0.0: {} - get-stream@8.0.1: {} - get-tsconfig@4.8.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -3406,9 +3572,14 @@ snapshots: domutils: 3.1.0 entities: 4.5.0 - human-id@1.0.2: {} + https-proxy-agent@7.0.4: + dependencies: + agent-base: 7.1.1 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color - human-signals@5.0.0: {} + human-id@1.0.2: {} iconv-lite@0.4.24: dependencies: @@ -3455,8 +3626,6 @@ snapshots: dependencies: '@types/estree': 1.0.5 - is-stream@3.0.0: {} - is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 @@ -3492,6 +3661,12 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -3540,7 +3715,7 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - merge-stream@2.0.0: {} + map-stream@0.1.0: {} merge2@1.4.1: {} @@ -3549,8 +3724,6 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 - mimic-fn@4.0.0: {} - minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -3561,6 +3734,13 @@ snapshots: minipass@7.1.2: {} + minizlib@3.0.1: + dependencies: + minipass: 7.1.2 + rimraf: 5.0.7 + + mkdirp@3.0.1: {} + mri@1.2.0: {} mrmime@2.0.0: {} @@ -3581,20 +3761,12 @@ snapshots: dependencies: whatwg-url: 5.0.0 - npm-run-path@5.3.0: - dependencies: - path-key: 4.0.0 - object-assign@4.1.1: {} once@1.4.0: dependencies: wrappy: 1.0.2 - onetime@6.0.0: - dependencies: - mimic-fn: 4.0.0 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -3655,8 +3827,6 @@ snapshots: path-key@3.1.1: {} - path-key@4.0.0: {} - path-parse@1.0.7: {} path-scurry@1.11.1: @@ -3670,14 +3840,28 @@ snapshots: pathval@2.0.0: {} + pause-stream@0.0.11: + dependencies: + through: 2.3.8 + picocolors@1.1.0: {} picomatch@2.3.1: {} + picomatch@4.0.2: {} + pify@4.0.1: {} pirates@4.0.6: {} + playwright-core@1.47.2: {} + + playwright@1.47.2: + dependencies: + playwright-core: 1.47.2 + optionalDependencies: + fsevents: 2.3.2 + postcss-load-config@3.1.4(postcss@8.4.45): dependencies: lilconfig: 2.1.0 @@ -3722,6 +3906,10 @@ snapshots: prettier@3.3.3: {} + ps-tree@1.2.0: + dependencies: + event-stream: 3.3.4 + pseudomap@1.0.2: {} pump@3.0.2: @@ -3766,6 +3954,10 @@ snapshots: reusify@1.0.4: {} + rimraf@5.0.7: + dependencies: + glob: 10.4.5 + rollup-plugin-esbuild@6.1.1(esbuild@0.21.5)(rollup@4.21.2): dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.21.2) @@ -3868,12 +4060,20 @@ snapshots: cross-spawn: 5.1.0 signal-exit: 3.0.7 + split@0.3.3: + dependencies: + through: 2.3.8 + sprintf-js@1.0.3: {} stackback@0.0.2: {} std-env@3.7.0: {} + stream-combiner@0.0.4: + dependencies: + duplexer: 0.1.2 + streamx@2.20.1: dependencies: fast-fifo: 1.3.2 @@ -3904,8 +4104,6 @@ snapshots: strip-bom@3.0.0: {} - strip-final-newline@3.0.0: {} - strip-json-comments@3.1.1: {} sucrase@3.35.0: @@ -3971,8 +4169,21 @@ snapshots: fast-fifo: 1.3.2 streamx: 2.20.1 + tar@7.4.0: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.1 + mkdirp: 3.0.1 + yallist: 5.0.0 + term-size@2.2.1: {} + terminate@2.8.0: + dependencies: + ps-tree: 1.2.0 + text-decoder@1.2.0: dependencies: b4a: 1.6.6 @@ -3987,6 +4198,22 @@ snapshots: dependencies: any-promise: 1.3.0 + through@2.3.8: {} + + tiged@3.0.0-rc.0: + dependencies: + colorette: 2.0.20 + enquirer: 2.4.1 + fs-extra: 11.2.0 + fuzzysearch: 1.0.3 + https-proxy-agent: 7.0.4 + mri: 1.2.0 + rimraf: 5.0.7 + tar: 7.4.0 + tiny-glob: 0.2.9 + transitivePeerDependencies: + - supports-color + tiny-glob@0.2.9: dependencies: globalyzer: 0.1.0 @@ -3998,6 +4225,11 @@ snapshots: tinyexec@0.3.0: {} + tinyglobby@0.2.6: + dependencies: + fdir: 6.3.0(picomatch@4.0.2) + picomatch: 4.0.2 + tinypool@1.0.1: {} tinyrainbow@1.2.0: {} @@ -4047,6 +4279,8 @@ snapshots: universalify@0.1.2: {} + universalify@2.0.1: {} + unplugin-isolated-decl@0.6.5(rollup@4.21.2)(typescript@5.6.2)(webpack-sources@3.2.3): dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.21.2) @@ -4076,12 +4310,11 @@ snapshots: optionalDependencies: typescript: 5.6.2 - vite-node@2.0.5(@types/node@22.5.4): + vite-node@2.1.2(@types/node@22.5.4): dependencies: cac: 6.7.14 debug: 4.3.7 pathe: 1.1.2 - tinyrainbow: 1.2.0 vite: 5.4.3(@types/node@22.5.4) transitivePeerDependencies: - '@types/node' @@ -4103,33 +4336,34 @@ snapshots: '@types/node': 22.5.4 fsevents: 2.3.3 - vitest@2.0.5(@types/node@22.5.4)(@vitest/ui@2.0.5): + vitest@2.1.2(@types/node@22.5.4)(@vitest/ui@2.1.2): dependencies: - '@ampproject/remapping': 2.3.0 - '@vitest/expect': 2.0.5 - '@vitest/pretty-format': 2.0.5 - '@vitest/runner': 2.0.5 - '@vitest/snapshot': 2.0.5 - '@vitest/spy': 2.0.5 - '@vitest/utils': 2.0.5 + '@vitest/expect': 2.1.2 + '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(vite@5.4.3(@types/node@22.5.4)) + '@vitest/pretty-format': 2.1.2 + '@vitest/runner': 2.1.2 + '@vitest/snapshot': 2.1.2 + '@vitest/spy': 2.1.2 + '@vitest/utils': 2.1.2 chai: 5.1.1 debug: 4.3.7 - execa: 8.0.1 magic-string: 0.30.12 pathe: 1.1.2 std-env: 3.7.0 tinybench: 2.9.0 + tinyexec: 0.3.0 tinypool: 1.0.1 tinyrainbow: 1.2.0 vite: 5.4.3(@types/node@22.5.4) - vite-node: 2.0.5(@types/node@22.5.4) + vite-node: 2.1.2(@types/node@22.5.4) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.5.4 - '@vitest/ui': 2.0.5(vitest@2.0.5) + '@vitest/ui': 2.1.2(vitest@2.1.2) transitivePeerDependencies: - less - lightningcss + - msw - sass - sass-embedded - stylus @@ -4180,6 +4414,8 @@ snapshots: yallist@2.1.2: {} + yallist@5.0.0: {} + yaml@1.10.2: {} yocto-queue@0.1.0: {} diff --git a/rollup.config.js b/rollup.config.js index f66a0e6c..e8bdebf5 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -130,6 +130,7 @@ export default [ getConfig('ast-tooling'), getConfig('create'), getConfig('core'), + getConfig('adder-testing-library'), getConfig('cli') ]; diff --git a/tsconfig.json b/tsconfig.json index 415c0abd..f509789e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "**/core/tests/**", "packages/create/templates/**", "packages/create/shared/**", + "packages/adder-tests/_snapshots/**", // Ignoring the rollup config so that the imported plugins can resolve their types correctly // (setting `moduleResolution: Node16` breaks them) "rollup.config.js"