From b3c0105cb0be8f4b3ce52d2b24ebe1ca6410e45c Mon Sep 17 00:00:00 2001 From: Quentin Deroubaix <139884126+quentinderoubaix@users.noreply.github.com> Date: Wed, 6 Mar 2024 09:47:32 +0100 Subject: [PATCH] feat: support specifying the tsconfig in svelte-package (#11698) closes #9373 Adds the option tsconfig to svelte-package, allowing to specify which tsconfig / jsconfig to use during the packaging. --- .changeset/clean-dots-switch.md | 5 +++ .../docs/30-advanced/70-packaging.md | 1 + packages/package/src/cli.js | 5 +++ packages/package/src/index.js | 25 +++++++---- packages/package/src/types.d.ts | 1 + packages/package/src/typescript.js | 42 ++++++++++++------- .../expected/runes.svelte.d.ts | 1 + .../expected/runes.svelte.js | 1 + .../fixtures/tsconfig-specified/package.json | 16 +++++++ .../src/lib/runes.svelte.ts | 2 + .../tsconfig-specified/svelte.config.js | 7 ++++ .../tsconfig-specified/tsconfig.build.json | 6 +++ .../fixtures/tsconfig-specified/tsconfig.json | 6 +++ packages/package/test/index.js | 4 ++ 14 files changed, 99 insertions(+), 23 deletions(-) create mode 100644 .changeset/clean-dots-switch.md create mode 100644 packages/package/test/fixtures/tsconfig-specified/expected/runes.svelte.d.ts create mode 100644 packages/package/test/fixtures/tsconfig-specified/expected/runes.svelte.js create mode 100644 packages/package/test/fixtures/tsconfig-specified/package.json create mode 100644 packages/package/test/fixtures/tsconfig-specified/src/lib/runes.svelte.ts create mode 100644 packages/package/test/fixtures/tsconfig-specified/svelte.config.js create mode 100644 packages/package/test/fixtures/tsconfig-specified/tsconfig.build.json create mode 100644 packages/package/test/fixtures/tsconfig-specified/tsconfig.json diff --git a/.changeset/clean-dots-switch.md b/.changeset/clean-dots-switch.md new file mode 100644 index 000000000000..863acd875a06 --- /dev/null +++ b/.changeset/clean-dots-switch.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/package": minor +--- + +feat: add option to specify the tsconfig/jsconfig diff --git a/documentation/docs/30-advanced/70-packaging.md b/documentation/docs/30-advanced/70-packaging.md index 602bce96860b..50f85516e16e 100644 --- a/documentation/docs/30-advanced/70-packaging.md +++ b/documentation/docs/30-advanced/70-packaging.md @@ -197,6 +197,7 @@ You should think carefully about whether or not the changes you make to your pac - `-i`/`--input` — the input directory which contains all the files of the package. Defaults to `src/lib` - `-o`/`--o` — the output directory where the processed files are written to. Your `package.json`'s `exports` should point to files inside there, and the `files` array should include that folder. Defaults to `dist` - `-t`/`--types` — whether or not to create type definitions (`d.ts` files). We strongly recommend doing this as it fosters ecosystem library quality. Defaults to `true` +- `--tsconfig` - the path to a tsconfig or jsconfig. When not provided, searches for the next upper tsconfig/jsconfig in the workspace path. ## Publishing diff --git a/packages/package/src/cli.js b/packages/package/src/cli.js index cc464eddfe33..4833dc489359 100644 --- a/packages/package/src/cli.js +++ b/packages/package/src/cli.js @@ -24,6 +24,10 @@ prog .option('-o, --output', 'Output directory', 'dist') .option('-t, --types', 'Emit type declarations', true) .option('-w, --watch', 'Rerun when files change', false) + .option( + '--tsconfig', + 'A path to a tsconfig or jsconfig file. When not provided, searches for the next upper tsconfig/jsconfig in the workspace path.' + ) .action(async (args) => { try { const config = await load_config(); @@ -42,6 +46,7 @@ prog cwd: process.cwd(), input: args.input ?? config.kit?.files?.lib ?? 'src/lib', output: args.output, + tsconfig: args.tsconfig, types: args.types, config }; diff --git a/packages/package/src/index.js b/packages/package/src/index.js index 1970464779bc..6d7450182643 100644 --- a/packages/package/src/index.js +++ b/packages/package/src/index.js @@ -22,7 +22,7 @@ export async function build(options) { * @param {(name: string, code: string) => void} analyse_code */ async function do_build(options, analyse_code) { - const { input, output, temp, extensions, alias } = normalize_options(options); + const { input, output, temp, extensions, alias, tsconfig } = normalize_options(options); if (!fs.existsSync(input)) { throw new Error(`${path.relative('.', input)} does not exist`); @@ -38,7 +38,7 @@ async function do_build(options, analyse_code) { } for (const file of files) { - await process_file(input, temp, file, options.config.preprocess, alias, analyse_code); + await process_file(input, temp, file, options.config.preprocess, alias, tsconfig, analyse_code); } rimraf(output); @@ -62,7 +62,7 @@ export async function watch(options) { validate(); - const { input, output, extensions, alias } = normalize_options(options); + const { input, output, extensions, alias, tsconfig } = normalize_options(options); const message = `\nWatching ${path.relative(options.cwd, input)} for changes...\n`; @@ -119,7 +119,15 @@ export async function watch(options) { if (type === 'add' || type === 'change') { console.log(`Processing ${file.name}`); try { - await process_file(input, output, file, options.config.preprocess, alias, analyse_code); + await process_file( + input, + output, + file, + options.config.preprocess, + alias, + tsconfig, + analyse_code + ); } catch (e) { errored = true; console.error(e); @@ -170,6 +178,7 @@ function normalize_options(options) { '__package__' ); const extensions = options.config.extensions ?? ['.svelte']; + const tsconfig = options.tsconfig ? path.resolve(options.cwd, options.tsconfig) : undefined; const alias = { $lib: path.resolve(options.cwd, options.config.kit?.files?.lib ?? 'src/lib'), @@ -181,7 +190,8 @@ function normalize_options(options) { output, temp, extensions, - alias + alias, + tsconfig }; } @@ -191,9 +201,10 @@ function normalize_options(options) { * @param {import('./types.js').File} file * @param {import('svelte/types/compiler/preprocess').PreprocessorGroup | undefined} preprocessor * @param {Record} aliases + * @param {string | undefined} tsconfig * @param {(name: string, code: string) => void} analyse_code */ -async function process_file(input, output, file, preprocessor, aliases, analyse_code) { +async function process_file(input, output, file, preprocessor, aliases, tsconfig, analyse_code) { const filename = path.join(input, file.name); const dest = path.join(output, file.dest); @@ -208,7 +219,7 @@ async function process_file(input, output, file, preprocessor, aliases, analyse_ } if (file.name.endsWith('.ts') && !file.name.endsWith('.d.ts')) { - contents = await transpile_ts(filename, contents); + contents = await transpile_ts(tsconfig, filename, contents); } contents = resolve_aliases(input, file.name, contents, aliases); diff --git a/packages/package/src/types.d.ts b/packages/package/src/types.d.ts index 6724137d8d1d..f575d4627cee 100644 --- a/packages/package/src/types.d.ts +++ b/packages/package/src/types.d.ts @@ -5,6 +5,7 @@ export interface Options { input: string; output: string; types: boolean; + tsconfig?: string; config: { extensions?: string[]; kit?: { diff --git a/packages/package/src/typescript.js b/packages/package/src/typescript.js index 5163676082e3..f2f7c31a8e79 100644 --- a/packages/package/src/typescript.js +++ b/packages/package/src/typescript.js @@ -62,12 +62,13 @@ export async function emit_dts(input, output, cwd, alias, files) { /** * TS -> JS * + * @param {string | undefined} tsconfig * @param {string} filename * @param {string} source */ -export async function transpile_ts(filename, source) { +export async function transpile_ts(tsconfig, filename, source) { const ts = await try_load_ts(); - const options = load_tsconfig(filename, ts); + const options = load_tsconfig(tsconfig, filename, ts); // transpileModule treats NodeNext as CommonJS because it doesn't read the package.json. Therefore we need to override it. // Also see https://github.com/microsoft/TypeScript/issues/53022 (the filename workaround doesn't work). return ts.transpileModule(source, { @@ -91,28 +92,37 @@ async function try_load_ts() { } /** + * @param {string | undefined} tsconfig * @param {string} filename * @param {import('typescript')} ts */ -function load_tsconfig(filename, ts) { +function load_tsconfig(tsconfig, filename, ts) { let config_filename; - // ts.findConfigFile is broken (it will favour a distant tsconfig - // over a near jsconfig, and then only when you call it twice) - // so we implement it ourselves - let dir = filename; - while (dir !== (dir = path.dirname(dir))) { - const tsconfig = path.join(dir, 'tsconfig.json'); - const jsconfig = path.join(dir, 'jsconfig.json'); - + if (tsconfig) { if (fs.existsSync(tsconfig)) { config_filename = tsconfig; - break; + } else { + throw new Error('Failed to locate provided tsconfig or jsconfig'); } - - if (fs.existsSync(jsconfig)) { - config_filename = jsconfig; - break; + } else { + // ts.findConfigFile is broken (it will favour a distant tsconfig + // over a near jsconfig, and then only when you call it twice) + // so we implement it ourselves + let dir = filename; + while (dir !== (dir = path.dirname(dir))) { + const tsconfig = path.join(dir, 'tsconfig.json'); + const jsconfig = path.join(dir, 'jsconfig.json'); + + if (fs.existsSync(tsconfig)) { + config_filename = tsconfig; + break; + } + + if (fs.existsSync(jsconfig)) { + config_filename = jsconfig; + break; + } } } diff --git a/packages/package/test/fixtures/tsconfig-specified/expected/runes.svelte.d.ts b/packages/package/test/fixtures/tsconfig-specified/expected/runes.svelte.d.ts new file mode 100644 index 000000000000..2b17805f4351 --- /dev/null +++ b/packages/package/test/fixtures/tsconfig-specified/expected/runes.svelte.d.ts @@ -0,0 +1 @@ +export declare const x = true; diff --git a/packages/package/test/fixtures/tsconfig-specified/expected/runes.svelte.js b/packages/package/test/fixtures/tsconfig-specified/expected/runes.svelte.js new file mode 100644 index 000000000000..30dc55a73230 --- /dev/null +++ b/packages/package/test/fixtures/tsconfig-specified/expected/runes.svelte.js @@ -0,0 +1 @@ +export const x = true; diff --git a/packages/package/test/fixtures/tsconfig-specified/package.json b/packages/package/test/fixtures/tsconfig-specified/package.json new file mode 100644 index 000000000000..6dac2ac90c18 --- /dev/null +++ b/packages/package/test/fixtures/tsconfig-specified/package.json @@ -0,0 +1,16 @@ +{ + "name": "typescript", + "private": true, + "version": "1.0.0", + "description": "typescript package using esnext", + "type": "module", + "peerDependencies": { + "svelte": "^4.0.0" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "svelte": "./dist/index.js" + } + } +} diff --git a/packages/package/test/fixtures/tsconfig-specified/src/lib/runes.svelte.ts b/packages/package/test/fixtures/tsconfig-specified/src/lib/runes.svelte.ts new file mode 100644 index 000000000000..edfdd5c0d60a --- /dev/null +++ b/packages/package/test/fixtures/tsconfig-specified/src/lib/runes.svelte.ts @@ -0,0 +1,2 @@ +// a comment to delete +export const x = true; diff --git a/packages/package/test/fixtures/tsconfig-specified/svelte.config.js b/packages/package/test/fixtures/tsconfig-specified/svelte.config.js new file mode 100644 index 000000000000..8e74287e522a --- /dev/null +++ b/packages/package/test/fixtures/tsconfig-specified/svelte.config.js @@ -0,0 +1,7 @@ +import preprocess from 'svelte-preprocess'; + +export default { + preprocess: preprocess({ + preserve: ['ld+json'] + }) +}; diff --git a/packages/package/test/fixtures/tsconfig-specified/tsconfig.build.json b/packages/package/test/fixtures/tsconfig-specified/tsconfig.build.json new file mode 100644 index 000000000000..644cc2ca2660 --- /dev/null +++ b/packages/package/test/fixtures/tsconfig-specified/tsconfig.build.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "removeComments": true + } +} diff --git a/packages/package/test/fixtures/tsconfig-specified/tsconfig.json b/packages/package/test/fixtures/tsconfig-specified/tsconfig.json new file mode 100644 index 000000000000..b7f8a03f204e --- /dev/null +++ b/packages/package/test/fixtures/tsconfig-specified/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext" + } +} diff --git a/packages/package/test/index.js b/packages/package/test/index.js index ec323577817b..73667f593266 100644 --- a/packages/package/test/index.js +++ b/packages/package/test/index.js @@ -151,6 +151,10 @@ test('SvelteKit interop', async () => { await test_make_package('svelte-kit'); }); +test('create package with tsconfig specified', async () => { + await test_make_package('tsconfig-specified', { tsconfig: 'tsconfig.build.json' }); +}); + // chokidar doesn't fire events in github actions :shrug: if (!process.env.CI) { test('watches for changes', async () => {