diff --git a/documentation/docs/30-add-ons/99-community.md b/documentation/docs/30-add-ons/99-community.md index 53c7996ba..225598d87 100644 --- a/documentation/docs/30-add-ons/99-community.md +++ b/documentation/docs/30-add-ons/99-community.md @@ -134,10 +134,10 @@ export default defineConfig({ And the global test setup script `tests/setup/global.js`: ```js -import { fileURLToPath } from 'node:url'; +import path from 'node:path'; import { setupGlobal } from 'sv/testing'; - -const TEST_DIR = fileURLToPath(new URL('../../.test-output/', import.meta.url)); +// test projects are scaffolded into `/.test-output` +const TEST_DIR = path.resolve(import.meta.dirname, '..', '..', '.test-output'); export default setupGlobal({ TEST_DIR }); ``` diff --git a/packages/sv-utils/src/tests/css/index.ts b/packages/sv-utils/src/tests/css/index.ts index 4a9032f55..b42f215ff 100644 --- a/packages/sv-utils/src/tests/css/index.ts +++ b/packages/sv-utils/src/tests/css/index.ts @@ -1,12 +1,11 @@ import fs from 'node:fs'; -import { join, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { join } from 'node:path'; import { format } from 'oxfmt'; import { describe, expect, test } from 'vitest'; import oxfmtConfig from '../../../../../oxfmt.config.ts'; import { parseCss, serializeCss } from '../../tooling/index.ts'; -const baseDir = resolve(fileURLToPath(import.meta.url), '..'); +const baseDir = import.meta.dirname; const categoryDirectories = getDirectoryNames(baseDir); for (const categoryDirectory of categoryDirectories) { diff --git a/packages/sv-utils/src/tests/html/index.ts b/packages/sv-utils/src/tests/html/index.ts index cf94e27f8..8fb0b5646 100644 --- a/packages/sv-utils/src/tests/html/index.ts +++ b/packages/sv-utils/src/tests/html/index.ts @@ -1,12 +1,11 @@ import fs from 'node:fs'; -import { join, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { join } from 'node:path'; import { format } from 'oxfmt'; import { describe, expect, test } from 'vitest'; import oxfmtConfig from '../../../../../oxfmt.config.ts'; import { parseHtml, serializeHtml } from '../../tooling/index.ts'; -const baseDir = resolve(fileURLToPath(import.meta.url), '..'); +const baseDir = import.meta.dirname; const categoryDirectories = getDirectoryNames(baseDir); for (const categoryDirectory of categoryDirectories) { diff --git a/packages/sv-utils/src/tests/js/index.ts b/packages/sv-utils/src/tests/js/index.ts index 191aaeac7..02c437e2d 100644 --- a/packages/sv-utils/src/tests/js/index.ts +++ b/packages/sv-utils/src/tests/js/index.ts @@ -1,10 +1,9 @@ import fs from 'node:fs'; -import { join, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { join } from 'node:path'; import { describe, expect, test } from 'vitest'; import { parseScript, serializeScript } from '../../tooling/index.ts'; -const baseDir = resolve(fileURLToPath(import.meta.url), '..'); +const baseDir = import.meta.dirname; const categoryDirectories = getDirectoryNames(baseDir); for (const categoryDirectory of categoryDirectories) { diff --git a/packages/sv-utils/src/tests/svelte/svelte/index.ts b/packages/sv-utils/src/tests/svelte/svelte/index.ts index b207b6f43..933c7b0c5 100644 --- a/packages/sv-utils/src/tests/svelte/svelte/index.ts +++ b/packages/sv-utils/src/tests/svelte/svelte/index.ts @@ -1,10 +1,9 @@ import fs from 'node:fs'; -import { join, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { join } from 'node:path'; import { describe, expect, test } from 'vitest'; import { parseSvelte, serializeSvelte } from '../../../../src/tooling/index.ts'; -const baseDir = resolve(fileURLToPath(import.meta.url), '..'); +const baseDir = import.meta.dirname; const categoryDirectories = getDirectoryNames(baseDir); for (const categoryDirectory of categoryDirectories) { diff --git a/packages/sv/src/addons/tests/_setup/global.ts b/packages/sv/src/addons/tests/_setup/global.ts index d5938b925..335ec458a 100644 --- a/packages/sv/src/addons/tests/_setup/global.ts +++ b/packages/sv/src/addons/tests/_setup/global.ts @@ -1,9 +1,12 @@ +import path from 'node:path'; import process from 'node:process'; -import { fileURLToPath } from 'node:url'; +import * as find from 'empathic/find'; import { setupGlobal } from 'sv/testing'; import { exec } from 'tinyexec'; -const TEST_DIR = fileURLToPath(new URL('../../../../.test-output/addons/', import.meta.url)); +const ROOT = path.dirname(find.up('pnpm-workspace.yaml', { cwd: import.meta.dirname })!); +const TEST_DIR = path.resolve(ROOT, 'packages', 'sv', '.test-output', 'addons'); + const CI = Boolean(process.env.CI); export default setupGlobal({ diff --git a/packages/sv/src/addons/tests/drizzle/test.ts b/packages/sv/src/addons/tests/drizzle/test.ts index 911fc0226..e794e3759 100644 --- a/packages/sv/src/addons/tests/drizzle/test.ts +++ b/packages/sv/src/addons/tests/drizzle/test.ts @@ -1,7 +1,6 @@ import fs from 'node:fs'; import path from 'node:path'; import process from 'node:process'; -import { fileURLToPath } from 'node:url'; import { execSync } from 'tinyexec'; import { beforeAll, expect } from 'vitest'; import drizzle from '../../drizzle.ts'; @@ -39,7 +38,7 @@ const { test, testCases, prepareServer } = setupTest( beforeAll(() => { if (!MUST_HAVE_DOCKER) return; - const cwd = path.dirname(fileURLToPath(import.meta.url)); + const cwd = import.meta.dirname; try { execSync('docker', ['--version'], { nodeOptions: { cwd }, throwOnError: true }); diff --git a/packages/sv/src/cli/tests/cli.ts b/packages/sv/src/cli/tests/cli.ts index 80d7b2316..dc84727e6 100644 --- a/packages/sv/src/cli/tests/cli.ts +++ b/packages/sv/src/cli/tests/cli.ts @@ -2,19 +2,20 @@ import fs from 'node:fs'; import path from 'node:path'; import process from 'node:process'; import { parse } from '@sveltejs/sv-utils'; +import * as find from 'empathic/find'; import { exec } from 'tinyexec'; import { beforeAll, describe, expect, it } from 'vitest'; /** Matches `sv@1.2.3`, `sv@0.0.0-next.0`, `sv@1.0.0-rc.1+build.5`. */ const SV_VERSION_REGEX = /sv@\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?/g; -const monoRepoPath = path.resolve(__dirname, '..', '..', '..', '..', '..'); -const svBinPath = path.resolve(monoRepoPath, 'packages', 'sv', 'dist', 'bin.mjs'); -const testOutputCliPath = path.resolve(monoRepoPath, 'packages', 'sv', '.test-output', 'cli'); +const ROOT = path.dirname(find.up('pnpm-workspace.yaml', { cwd: import.meta.dirname })!); +const SV_BIN_PATH = path.resolve(ROOT, 'packages', 'sv', 'dist', 'bin.mjs'); +const TEST_DIR = path.resolve(ROOT, 'packages', 'sv', '.test-output', 'cli'); beforeAll(() => { - if (fs.existsSync(testOutputCliPath)) { - fs.rmSync(testOutputCliPath, { force: true, recursive: true }); + if (fs.existsSync(TEST_DIR)) { + fs.rmSync(TEST_DIR, { force: true, recursive: true }); } }); @@ -72,15 +73,12 @@ describe('cli', () => { snapshot?: boolean; }; - const testOutputPath = path.relative( - monoRepoPath, - path.resolve(testOutputCliPath, projectName) - ); + const projectPath = path.relative(ROOT, path.resolve(TEST_DIR, projectName)); const allArgs = [ - svBinPath, + SV_BIN_PATH, 'create', - testOutputPath, + projectPath, '--template', template, ...(template === 'addon' ? [] : ['--types', 'ts']), @@ -89,12 +87,12 @@ describe('cli', () => { ]; /** - * Same as `exec`. but `cwd` defaults to `testOutputPath` + * Same as `exec`. but `cwd` defaults to `projectPath` */ const run = (...params: Parameters) => { const [command, args, options = {}] = params; options.nodeOptions ??= {}; - options.nodeOptions.cwd ??= testOutputPath; + options.nodeOptions.cwd ??= projectPath; return exec(command, args, options); }; @@ -108,15 +106,15 @@ describe('cli', () => { `Error with cli:\n cmd: node ${allArgs.join(' ')}\n stdout: ${result.stdout}\n stderr: ${result.stderr}` ).toBe(0); // test output path exists - expect(fs.existsSync(testOutputPath)).toBe(true); + expect(fs.existsSync(projectPath)).toBe(true); // package.json has a name - const packageJsonPath = path.resolve(testOutputPath, 'package.json'); + const packageJsonPath = path.resolve(projectPath, 'package.json'); const { data: packageJson } = parse.json(fs.readFileSync(packageJsonPath, 'utf-8')); expect(packageJson.name).toBe(projectName); const snapPath = path.resolve( - monoRepoPath, + ROOT, 'packages', 'sv', 'src', @@ -126,7 +124,7 @@ describe('cli', () => { projectName ); const relativeFiles = snapshot - ? (fs.readdirSync(testOutputPath, { recursive: true }) as string[]) + ? (fs.readdirSync(projectPath, { recursive: true }) as string[]) : []; // Files from ai-tools repo (skills, agents) change independently - @@ -135,7 +133,7 @@ describe('cli', () => { const aiToolsPattern = /[\\/](skills|agents)[\\/]/; for (const relativeFile of relativeFiles) { - if (!fs.statSync(path.resolve(testOutputPath, relativeFile)).isFile()) continue; + if (!fs.statSync(path.resolve(projectPath, relativeFile)).isFile()) continue; if (['.svg', '.env'].some((ext) => relativeFile.endsWith(ext))) continue; const normalized = relativeFile.replace(/\\/g, '/'); @@ -151,7 +149,7 @@ describe('cli', () => { continue; } - let generated = fs.readFileSync(path.resolve(testOutputPath, relativeFile), 'utf-8'); + let generated = fs.readFileSync(path.resolve(projectPath, relativeFile), 'utf-8'); if (relativeFile === 'package.json') { const { data: generatedPackageJson } = parse.json(generated); // remove @types/node from generated package.json as we test on different node versions @@ -220,7 +218,7 @@ describe('cli', () => { } if (projectName === 'create-experimental') { - const read = (p: string) => fs.readFileSync(path.resolve(testOutputPath, p), 'utf-8'); + const read = (p: string) => fs.readFileSync(path.resolve(projectPath, p), 'utf-8'); const envFile = read('src/env.ts'); expect(envFile).toContain('defineEnvVars'); expect(envFile).toContain('DATABASE_URL'); @@ -231,7 +229,7 @@ describe('cli', () => { if (template === 'addon') { // replace sv and sv-utils versions in package.json for tests - const packageJsonPath = path.resolve(testOutputPath, 'package.json'); + const packageJsonPath = path.resolve(projectPath, 'package.json'); const { data: packageJson } = parse.json(fs.readFileSync(packageJsonPath, 'utf-8')); packageJson.peerDependencies['sv'] = 'file:../../../..'; packageJson.devDependencies['sv'] = 'file:../../../..'; diff --git a/packages/sv/src/cli/tests/snapshots/@my-org/sv/tests/setup/global.js b/packages/sv/src/cli/tests/snapshots/@my-org/sv/tests/setup/global.js index 1f9a11ce1..e8100be3f 100644 --- a/packages/sv/src/cli/tests/snapshots/@my-org/sv/tests/setup/global.js +++ b/packages/sv/src/cli/tests/snapshots/@my-org/sv/tests/setup/global.js @@ -1,7 +1,7 @@ -import { fileURLToPath } from 'node:url'; +import path from 'node:path'; import { setupGlobal } from 'sv/testing'; -const TEST_DIR = fileURLToPath(new URL('../../.test-output/', import.meta.url)); +const TEST_DIR = path.resolve(import.meta.dirname, '..', '..', '.test-output'); export default setupGlobal({ TEST_DIR, diff --git a/packages/sv/src/core/fetch-packages.ts b/packages/sv/src/core/fetch-packages.ts index f1d1a32d2..181425c5b 100644 --- a/packages/sv/src/core/fetch-packages.ts +++ b/packages/sv/src/core/fetch-packages.ts @@ -2,7 +2,6 @@ import fs from 'node:fs'; import { platform } from 'node:os'; import path from 'node:path'; import { pipeline } from 'node:stream/promises'; -import { fileURLToPath } from 'node:url'; import { createGunzip } from 'node:zlib'; import { color, coerceVersion, downloadJson } from '@sveltejs/sv-utils'; import { unpackTar } from 'modern-tar/fs'; @@ -13,7 +12,7 @@ import { PackageJSONSchema, type PackageJSON } from './common.ts'; import type { AddonDefinition, AddonReference } from './config.ts'; // path to the `node_modules` directory of `sv` -const NODE_MODULES = fileURLToPath(new URL('../../node_modules', import.meta.url)); +const NODE_MODULES = path.resolve(import.meta.dirname, '..', '..', 'node_modules'); type PackageBlocklist = { npm_names: string[] }; diff --git a/packages/sv/src/create/scripts/build-templates.js b/packages/sv/src/create/scripts/build-templates.js index 3ff6dd22b..d52ef5253 100644 --- a/packages/sv/src/create/scripts/build-templates.js +++ b/packages/sv/src/create/scripts/build-templates.js @@ -2,7 +2,6 @@ import fs from 'node:fs'; import { createRequire } from 'node:module'; import path from 'node:path'; -import { fileURLToPath } from 'node:url'; import parser from 'gitignore-parser'; import { format } from 'oxfmt'; import { transform } from 'sucrase'; @@ -11,7 +10,7 @@ import oxfmtConfig from '../../../../../oxfmt.config.ts'; /** @import { File, LanguageType } from '../index.ts' */ -const pkgRoot = path.resolve(fileURLToPath(import.meta.url), '..', '..'); +const pkgRoot = path.resolve(import.meta.dirname, '..'); const require = createRequire(import.meta.url); const createVitePath = path.dirname(require.resolve('create-vite/package.json')); diff --git a/packages/sv/src/create/templates/addon/tests/setup/global.js b/packages/sv/src/create/templates/addon/tests/setup/global.js index 1f9a11ce1..e8100be3f 100644 --- a/packages/sv/src/create/templates/addon/tests/setup/global.js +++ b/packages/sv/src/create/templates/addon/tests/setup/global.js @@ -1,7 +1,7 @@ -import { fileURLToPath } from 'node:url'; +import path from 'node:path'; import { setupGlobal } from 'sv/testing'; -const TEST_DIR = fileURLToPath(new URL('../../.test-output/', import.meta.url)); +const TEST_DIR = path.resolve(import.meta.dirname, '..', '..', '.test-output'); export default setupGlobal({ TEST_DIR, diff --git a/packages/sv/src/create/tests/check.ts b/packages/sv/src/create/tests/create.ts similarity index 78% rename from packages/sv/src/create/tests/check.ts rename to packages/sv/src/create/tests/create.ts index 5f428a430..104d2689f 100644 --- a/packages/sv/src/create/tests/check.ts +++ b/packages/sv/src/create/tests/create.ts @@ -1,31 +1,29 @@ import fs from 'node:fs'; import path from 'node:path'; -import { fileURLToPath } from 'node:url'; +import * as find from 'empathic/find'; import { exec, type Result } from 'tinyexec'; import { beforeAll, describe, expect, test } from 'vitest'; import { add, officialAddons } from '../../../../sv/src/index.ts'; import { createProject } from '../../cli/create.ts'; import { type LanguageType, type TemplateType, create } from '../index.ts'; -// Resolve the given path relative to the current file -const resolve_path = (path: string) => fileURLToPath(new URL(path, import.meta.url)); - -// use a directory outside of packages to ensure it isn't added to the pnpm workspace -const test_workspace_dir = resolve_path('../../../../../.test-output/create/'); +const ROOT = path.dirname(find.up('pnpm-workspace.yaml', { cwd: import.meta.dirname })!); +const TEMPLATES_DIR = path.resolve(ROOT, 'packages', 'sv', 'src', 'create', 'templates'); +const TEST_DIR = path.resolve(ROOT, 'packages', 'sv', '.test-output', 'create'); // prepare test pnpm workspace -fs.rmSync(test_workspace_dir, { recursive: true, force: true }); -fs.mkdirSync(test_workspace_dir, { recursive: true }); +fs.rmSync(TEST_DIR, { recursive: true, force: true }); +fs.mkdirSync(TEST_DIR, { recursive: true }); -fs.writeFileSync(path.join(test_workspace_dir, 'pnpm-workspace.yaml'), 'packages:\n - ./*\n'); +fs.writeFileSync(path.join(TEST_DIR, 'pnpm-workspace.yaml'), 'packages:\n - ./*\n'); beforeAll(async () => { const install = await exec('pnpm', ['install', '--no-frozen-lockfile'], { - nodeOptions: { cwd: test_workspace_dir } + nodeOptions: { cwd: TEST_DIR } }); if (install.exitCode !== 0) { throw new Error( - `pnpm install failed in ${test_workspace_dir}\n stdout: ${install.stdout}\n stderr: ${install.stderr}` + `pnpm install failed in ${TEST_DIR}\n stdout: ${install.stdout}\n stderr: ${install.stderr}` ); } }, 60000); @@ -36,13 +34,13 @@ beforeAll(async () => { */ const script_test_map = new Map Result]>>(); -const templates = fs.readdirSync(resolve_path('../templates/')) as TemplateType[]; +const templates = fs.readdirSync(TEMPLATES_DIR) as TemplateType[]; for (const template of templates.filter((t) => t !== 'addon')) { if (template[0] === '.') continue; for (const types of ['checkjs', 'typescript', 'none'] as LanguageType[]) { - const cwd = path.join(test_workspace_dir, `${template}-${types}`); + const cwd = path.join(TEST_DIR, `${template}-${types}`); fs.rmSync(cwd, { recursive: true, force: true }); if (template === 'demo' && types === 'typescript') { diff --git a/packages/sv/src/create/tests/playground.ts b/packages/sv/src/create/tests/playground.ts index 039dccc45..074aad9c0 100644 --- a/packages/sv/src/create/tests/playground.ts +++ b/packages/sv/src/create/tests/playground.ts @@ -1,6 +1,6 @@ import * as fs from 'node:fs'; import path from 'node:path'; -import { fileURLToPath } from 'node:url'; +import * as find from 'empathic/find'; import { expect, test } from 'vitest'; import { create } from '../index.ts'; import { @@ -11,8 +11,8 @@ import { validatePlaygroundUrl } from '../playground.ts'; -const resolvePath = (path: string) => fileURLToPath(new URL(path, import.meta.url)); -const testWorkspaceDir = resolvePath('../../../.test-output/create/'); +const ROOT = path.dirname(find.up('pnpm-workspace.yaml', { cwd: import.meta.dirname })!); +const TEST_DIR = path.resolve(ROOT, 'packages', 'sv', '.test-output', 'playground'); test.for([ { input: 'https://svelte.dev/playground/628f435d787a465f9c1f1854134d6f70/', valid: true }, @@ -147,15 +147,15 @@ test('detect dependencies from playground files', () => { expect(Array.from(dependencies.keys()).length).toBe(3); }); -test('real world download and convert playground async', async () => { - const directory = path.join(testWorkspaceDir, 'real-world-playground'); +test('download and convert playground e2e (svelte async)', async () => { + const directory = path.join(TEST_DIR, 'playground-e2e-svelte-async'); if (fs.existsSync(directory)) { fs.rmSync(directory, { recursive: true }); } create({ cwd: directory, - name: 'real-world-playground', + name: 'playground-e2e-svelte-async', template: 'minimal', types: 'typescript' }); @@ -199,15 +199,15 @@ test('real world download and convert playground async', async () => { expect(viteConfigContent).toContain('experimental: { async: true }'); }); -test('real world download and convert playground without async', async () => { - const directory = path.join(testWorkspaceDir, 'real-world-playground-old'); +test('download and convert playground e2e (pre svelte async)', async () => { + const directory = path.join(TEST_DIR, 'playground-e2e-pre-svelte-async'); if (fs.existsSync(directory)) { fs.rmSync(directory, { recursive: true }); } create({ cwd: directory, - name: 'real-world-playground-old', + name: 'playground-e2e-pre-svelte-async', template: 'minimal', types: 'typescript' }); diff --git a/packages/sv/src/create/utils.ts b/packages/sv/src/create/utils.ts index cd34448dc..6ff32598d 100644 --- a/packages/sv/src/create/utils.ts +++ b/packages/sv/src/create/utils.ts @@ -1,6 +1,5 @@ import fs from 'node:fs'; import path from 'node:path'; -import { fileURLToPath } from 'node:url'; import { isNodeError } from '../core/common.ts'; import type { Common } from './index.ts'; @@ -58,14 +57,12 @@ export function copy( } } -export function dist(path: string): string { +export function dist(currentPath: string): string { // we need to make this check, because vitest is making the package root the cwd, // but executing the cli from the command line already makes the dist folder the cwd. const insideDistFolder = import.meta.url.includes('dist'); - return fileURLToPath( - new URL(`./${!insideDistFolder ? 'dist/' : ''}${path}`, import.meta.url).href - ); + return path.resolve(import.meta.dirname, insideDistFolder ? '' : 'dist', currentPath); } export function getSharedFiles(): Common['files'] { diff --git a/packages/sv/src/migrate/migrations/tests.spec.ts b/packages/sv/src/migrate/migrations/tests.spec.ts index b5bbca775..eb0ae9efb 100644 --- a/packages/sv/src/migrate/migrations/tests.spec.ts +++ b/packages/sv/src/migrate/migrations/tests.spec.ts @@ -1,11 +1,10 @@ import fs from 'node:fs'; -import path, { resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; +import path from 'node:path'; import { describe, expect, test } from 'vitest'; import { prepareSvApi } from '../../core/engine.ts'; import { createWorkspace } from '../../core/workspace.ts'; -const baseDir = resolve(fileURLToPath(import.meta.url), '..'); +const baseDir = import.meta.dirname; const migrationDirectories = getDirectoryNames(baseDir); const tasksDirectoryName = 'tasks'; const testsDirectoryName = 'tests'; diff --git a/scripts/generate-api-surface.js b/scripts/generate-api-surface.js index da4c7934e..2c2c503e1 100644 --- a/scripts/generate-api-surface.js +++ b/scripts/generate-api-surface.js @@ -12,11 +12,10 @@ import fs from 'node:fs'; import path from 'node:path'; import process from 'node:process'; -import { fileURLToPath } from 'node:url'; import { format } from 'oxfmt'; import oxfmtConfig from '../oxfmt.config.ts'; -const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const ROOT = path.resolve(import.meta.dirname, '..'); const packages = [ { @@ -225,7 +224,7 @@ export async function generateApiSurface() { return generated; } -const isMain = process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1]); +const isMain = process.argv[1] && import.meta.filename === path.resolve(process.argv[1]); if (isMain) { generateApiSurface().catch((err) => {