diff --git a/.changeset/clever-pianos-wonder.md b/.changeset/clever-pianos-wonder.md new file mode 100644 index 000000000000..b11c2e595233 --- /dev/null +++ b/.changeset/clever-pianos-wonder.md @@ -0,0 +1,5 @@ +--- +'svelte-migrate': minor +--- + +feat: add `@sveltejs/package` migration (v1->v2) diff --git a/.changeset/few-donuts-admire.md b/.changeset/few-donuts-admire.md new file mode 100644 index 000000000000..d36a9a353912 --- /dev/null +++ b/.changeset/few-donuts-admire.md @@ -0,0 +1,5 @@ +--- +'create-svelte': major +--- + +breaking: update library scaffolding for `@sveltejs/package` version 2 diff --git a/.changeset/plenty-points-provide.md b/.changeset/plenty-points-provide.md new file mode 100644 index 000000000000..a6a2c8da2e0c --- /dev/null +++ b/.changeset/plenty-points-provide.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/package': major +--- + +breaking: remove `package.json` generation and package options from `svelte.config.js`. New default output directory is `dist`. Read the migration guide at https://github.com/sveltejs/kit/pull/8922 to learn how to update diff --git a/documentation/docs/30-advanced/70-packaging.md b/documentation/docs/30-advanced/70-packaging.md index 38d17a9c32b0..f2dc9bf66502 100644 --- a/documentation/docs/30-advanced/70-packaging.md +++ b/documentation/docs/30-advanced/70-packaging.md @@ -2,33 +2,105 @@ title: Packaging --- -> `svelte-package` is currently experimental. Non-backward compatible changes may occur in any future release. - You can use SvelteKit to build apps as well as component libraries, using the `@sveltejs/package` package (`npm create svelte` has an option to set this up for you). When you're creating an app, the contents of `src/routes` is the public-facing stuff; [`src/lib`](modules#$lib) contains your app's internal library. -A component library has the exact same structure as a SvelteKit app, except that `src/lib` is the public-facing bit. `src/routes` might be a documentation or demo site that accompanies the library, or it might just be a sandbox you use during development. +A component library has the exact same structure as a SvelteKit app, except that `src/lib` is the public-facing bit, and your root `package.json` is used to publish the package. `src/routes` might be a documentation or demo site that accompanies the library, or it might just be a sandbox you use during development. + +Running the `svelte-package` command from `@sveltejs/package` will take the contents of `src/lib` and generate a `dist` directory (which can be [configured](#options)) containing the following: + +- All the files in `src/lib`. Svelte components will be preprocessed, TypeScript files will be transpiled to JavaScript. +- Type definitions (`d.ts` files) which are generated for Svelte, JavaScript and TypeScript files. You need to install `typescript >= 4.0.0` for this. Type definitions are placed next to their implementation, hand-written `d.ts` files are copied over as is. You can [disable generation](#options), but we strongly recommend against it — people using your library might use TypeScript, for which they require these type definition files. + +> `@sveltejs/package` version 1 generated a `package.json`. This is no longer the case and it will now use the `package.json` from your project and validate that it is correct instead. If you're still on version 1, see [this PR](https://github.com/sveltejs/kit/pull/8922) for migration instructions. + +## Anatomy of a package.json + +Since you're now building a library for public use, the contents of your `package.json` will become more important. Through it, you configure the entry points of your package, which files are published to npm, and which dependencies your library has. Let's go through the most important fields one by one. + +### name + +This is the name of your package. It will be available for others to install using that name, and visible on `https://npmjs.com/package/`. + +```json +{ + "name": "your-library" +} +``` + +Read more about it [here](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#name). + +### license + +Every package should have a license field so people know how they are allowed to use it. A very popular license which is also very permissive in terms of distribution and reuse without warranty is `MIT`. + +```json +{ + "license": "MIT" +} +``` + +Read more about it [here](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#license). Note that you should also include a `LICENSE` file in your package. + +### files + +This tells npm which files it will pack up and upload to npm. It should contain your output folder (`dist` by default). Your `package.json` and `README` and `LICENSE` will always be included, so you don't need to specify them. + +```json +{ + "files": ["dist"] +} +``` + +To exclude unnecessary files (such as unit tests, or modules that are only imported from `src/routes` etc) you can add them to an `.npmignore` file. This will result in smaller packages that are faster to install. -Running the `svelte-package` command from `@sveltejs/package` will take the contents of `src/lib` and generate a `package` directory (which can be [configured](configuration)) containing the following: +Read more about it [here](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#files). -- All the files in `src/lib`, unless you [configure](configuration) custom `include`/`exclude` options. Svelte components will be preprocessed, TypeScript files will be transpiled to JavaScript. -- Type definitions (`d.ts` files) which are generated for Svelte, JavaScript and TypeScript files. You need to install `typescript >= 4.0.0` for this. Type definitions are placed next to their implementation, hand-written `d.ts` files are copied over as is. You can [disable generation](configuration), but we strongly recommend against it — people using your library might use TypeScript, for which they require these type definition files. -- A `package.json` copied from the project root with all fields except `"scripts"`, `"publishConfig.directory"` and `"publishConfig.linkDirectory"`. The `"dependencies"` field is included, which means you should add packages that you only need for your documentation or demo site to `"devDependencies"`. A `"type": "module"` and an `"exports"` field will be added if it's not defined in the original file. +### exports -The `"exports"` field contains the package's entry points. By default, all files in `src/lib` will be treated as an entry point unless they start with (or live in a directory that starts with) an underscore, but you can [configure](configuration) this behaviour. If you have a `src/lib/index.js` or `src/lib/index.svelte` file, it will be treated as the package root. +The `"exports"` field contains the package's entry points. If you set up a new library project through `npm create svelte@latest`, it's set to a single export, the package root: -For example, if you had a `src/lib/Foo.svelte` component and a `src/lib/index.js` module that re-exported it, a consumer of your library could do either of the following: +```json +{ + "exports": { + ".": { + "types": "./dist/index.d.ts", + "svelte": "./dist/index.js" + } + } +} +``` + +This tells bundlers and tooling that your package only has one entry point, the root, and everything should be imported through that, like this: ```js -// @filename: ambient.d.ts -declare module 'your-library'; +// @errors: 2307 +import { Something } from 'your-library'; +``` -// @filename: index.js -// ---cut--- -import { Foo } from 'your-library'; +The `types` and `svelte` keys are [export conditions](https://nodejs.org/api/packages.html#conditional-exports). They tell tooling what file to import when they look up the `your-library` import: + +- TypeScript sees the `types` condition and looks up the type definition file. If you don't publish type definitions, omit this condition. +- Svelte-aware tooling sees the `svelte` condition and knows this is a Svelte component library. If you publish a library that does not export any Svelte components and that could also work in non-Svelte projects (for example a Svelte store library), you can replace this condition with `default`. + +> Previous versions of `@sveltejs/package` also added a `package.json` export. This is no longer part of the template because all tooling can now deal with a `package.json` not being explicitly exported. + +You can adjust `exports` to your liking and provide more entry points. For example, if instead of a `src/lib/index.js` file that re-exported components you wanted to expose a `src/lib/Foo.svelte` component directly, you could create the following export map... + +```json +{ + "exports": { + "./Foo.svelte": { + "types": "./dist/Foo.svelte.d.ts", + "svelte": "./dist/Foo.svelte" + } + } +} ``` +...and a consumer of your library could import the component like so: + ```js // @filename: ambient.d.ts declare module 'your-library/Foo.svelte'; @@ -38,26 +110,78 @@ declare module 'your-library/Foo.svelte'; import Foo from 'your-library/Foo.svelte'; ``` -> You should avoid using [SvelteKit-specific modules](modules) like `$app` in your packages unless you intend for them to only be consumable by other SvelteKit projects. E.g. rather than using `import { browser } from '$app/environment'` you could use [`import.meta.env.SSR`](https://vitejs.dev/guide/env-and-mode.html#env-variables) to make the library available to all Vite-based projects or better yet use [Node conditional exports](https://nodejs.org/api/packages.html#conditional-exports) to make it work for all bundlers. You may also wish to pass in things like the current URL or a navigation action as a prop rather than relying directly on `$app/stores`, `$app/navigation`, etc. Writing your app in this more generic fashion will also make it easier to setup tools for testing, UI demos and so on. +In general, each key of the exports map is the path the user will have to use to import something from your package, and the value is the path to the file that will be imported or a map of export conditions which in turn contains these file paths. + +Read more about `exports` [here](https://nodejs.org/docs/latest-v18.x/api/packages.html#package-entry-points). + +### svelte + +This is a legacy field that enabled tooling to recognise Svelte component libraries. It's no longer necessary when using the `svelte` [export condition](#anatomy-of-a-package-json-exports), but for backwards compatibility with outdated tooling that doesn't yet know about export conditions it's good to keep it around. It should point towards your root entry point. + +```json +{ + "svelte": "./dist/index.js" +} +``` + +## Best practices + +You should avoid using [SvelteKit-specific modules](modules) like `$app` in your packages unless you intend for them to only be consumable by other SvelteKit projects. E.g. rather than using `import { browser } from '$app/environment'` you could use `import { BROWSER } from 'esm-env'` ([see esm-env docs](https://github.com/benmccann/esm-env)). You may also wish to pass in things like the current URL or a navigation action as a prop rather than relying directly on `$app/stores`, `$app/navigation`, etc. Writing your app in this more generic fashion will also make it easier to setup tools for testing, UI demos and so on. + +Ensure that you add [aliases](/docs/configuration#alias) via `svelte.config.js` (not `vite.config.js` or `tsconfig.json`), so that they are processed by `svelte-package`. + +You should think carefully about whether or not the changes you make to your package are a bug fix, a new feature, or a breaking change, and update the package version accordingly. Note that if you remove any paths from `exports` or any `export` conditions inside them from your existing library, that should be regarded as a breaking change. + +```diff +{ + "exports": { + ".": { + "types": "./dist/index.d.ts", +// changing `svelte` to `default` is a breaking change: +- "svelte": "./dist/index.js" ++ "default": "./dist/index.js" + }, +// removing this is a breaking change: +- "./foo": { +- "types": "./dist/foo.d.ts", +- "svelte": "./dist/foo.js", +- "default": "./dist/foo.js" +- }, +// adding this is ok: ++ "./bar": { ++ "types": "./dist/bar.d.ts", ++ "svelte": "./dist/bar.js", ++ "default": "./dist/bar.js" ++ } + } +} +``` ## Options `svelte-package` accepts the following options: - `-w`/`--watch` — watch files in `src/lib` for changes and rebuild the package +- `-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. You `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` ## Publishing To publish the generated package: ```sh -npm publish ./package +npm publish ``` -The `./package` above is referring to the directory name generated, change accordingly if you configure a custom [`package.dir`](configuration). - ## Caveats -All relative file imports need to be fully specified, adhering to Node's ESM algorithm. This means you cannot import the file `src/lib/something/index.js` like `import { something } from './something`, instead you need to import it like this: `import { something } from './something/index.js`. If you are using TypeScript, you need to import `.ts` files the same way, but using a `.js` file ending, _not_ a `.ts` file ending (this isn't under our control, the TypeScript team has made that decision). Setting `"moduleResolution": "NodeNext"` in your `tsconfig.json` or `jsconfig.json` will help you with this. +All relative file imports need to be fully specified, adhering to Node's ESM algorithm. This means that for a file like `src/lib/something/index.js`, you must include the filename with the extension: + +```diff +-import { something } from './something'; ++import { something } from './something/index.js'; + +If you are using TypeScript, you need to import `.ts` files the same way, but using a `.js` file ending, _not_ a `.ts` file ending. (This is a TypeScript design decision outside our control.) Setting `"moduleResolution": "NodeNext"` in your `tsconfig.json` or `jsconfig.json` will help you with this. -This is a relatively experimental feature and is not yet fully implemented. All files except Svelte files (preprocessed) and TypeScript files (transpiled to JavaScript) are copied across as-is. +All files except Svelte files (preprocessed) and TypeScript files (transpiled to JavaScript) are copied across as-is. diff --git a/packages/create-svelte/templates/skeletonlib/.meta.json b/packages/create-svelte/templates/skeletonlib/.meta.json index 17f168aca33c..aa67986ab50e 100644 --- a/packages/create-svelte/templates/skeletonlib/.meta.json +++ b/packages/create-svelte/templates/skeletonlib/.meta.json @@ -1,4 +1,4 @@ { - "title": "Library skeleton project", + "title": "Library project", "description": "Barebones scaffolding for your new Svelte library" } diff --git a/packages/create-svelte/templates/skeletonlib/package.template.json b/packages/create-svelte/templates/skeletonlib/package.template.json index e6ac754c6516..0534ade9a289 100644 --- a/packages/create-svelte/templates/skeletonlib/package.template.json +++ b/packages/create-svelte/templates/skeletonlib/package.template.json @@ -3,17 +3,31 @@ "version": "0.0.1", "scripts": { "dev": "vite dev", - "build": "svelte-kit sync && svelte-package", - "prepublishOnly": "echo 'Did you mean to publish `./package/`, instead of `./`?' && exit 1" + "build": "vite build && npm run package", + "preview": "vite preview", + "package": "svelte-kit sync && svelte-package && publint", + "prepublishOnly": "npm run package && publint" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "svelte": "./dist/index.js" + } + }, + "peerDependencies": { + "svelte": "^3.54.0" }, "devDependencies": { "@sveltejs/adapter-auto": "^2.0.0", "@sveltejs/kit": "^1.5.0", "@sveltejs/package": "^1.0.0", + "publint": "^0.1.9", "svelte": "^3.54.0", "tslib": "^2.4.1", "typescript": "^4.9.3", "vite": "^4.0.0" }, + "svelte": "./dist/index.js", + "types": "./dist/index.d.ts", "type": "module" } diff --git a/packages/migrate/migrations/package/index.js b/packages/migrate/migrations/package/index.js new file mode 100644 index 000000000000..ff193a25c221 --- /dev/null +++ b/packages/migrate/migrations/package/index.js @@ -0,0 +1,80 @@ +import fs from 'fs'; +import colors from 'kleur'; +import path from 'path'; +import prompts from 'prompts'; +import { pathToFileURL } from 'url'; +import { bail, check_git } from '../../utils.js'; +import { migrate_config } from './migrate_config.js'; +import { migrate_pkg } from './migrate_pkg.js'; + +export async function migrate() { + if (!fs.existsSync('svelte.config.js')) { + bail('Please re-run this script in a directory with a svelte.config.js'); + } + if (!fs.existsSync('package.json')) { + bail('Please re-run this script in a directory with a package.json'); + } + + console.log( + colors + .bold() + .yellow( + '\nThis will update your svelte.config.js and package.json in the current directory\n' + ) + ); + + const use_git = check_git(); + + const response = await prompts({ + type: 'confirm', + name: 'value', + message: 'Continue?', + initial: false + }); + + if (!response.value) { + process.exit(1); + } + + const { default: config } = await import(pathToFileURL(path.resolve('svelte.config.js')).href); + const has_package_config = !!config.package; + + config.package = { + source: path.resolve(config.kit?.files?.lib ?? config.package?.source ?? 'src/lib'), + dir: config.package?.dir ?? 'package', + exports: + config.package?.exports ?? + ((/** @type {string} */ filepath) => !/^_|\/_|\.d\.ts$/.test(filepath)), + files: config.package?.files ?? (() => true), + emitTypes: config.package?.emitTypes ?? true + }; + config.extensions = config.extensions ?? ['.svelte']; + + migrate_pkg(config); + + if (has_package_config) { + migrate_config(); + } + + console.log(colors.bold().green('✔ Your project has been migrated')); + + console.log('\nRecommended next steps:\n'); + + const cyan = colors.bold().cyan; + + const tasks = [ + use_git && cyan('git commit -m "migration to @sveltejs/package v2"'), + `Review the migration guide at https://github.com/sveltejs/kit/pull/8922`, + `Read the updated docs at https://kit.svelte.dev/docs/packaging` + ].filter(Boolean); + + tasks.forEach((task, i) => { + console.log(` ${i + 1}: ${task}`); + }); + + console.log(''); + + if (use_git) { + console.log(`Run ${cyan('git diff')} to review changes.\n`); + } +} diff --git a/packages/migrate/migrations/package/migrate_config.js b/packages/migrate/migrations/package/migrate_config.js new file mode 100644 index 000000000000..63e5df62dbc3 --- /dev/null +++ b/packages/migrate/migrations/package/migrate_config.js @@ -0,0 +1,81 @@ +import fs from 'fs'; +import colors from 'kleur'; +import MagicString from 'magic-string'; +import ts from 'typescript'; + +export function migrate_config() { + try { + const content = fs.readFileSync('svelte.config.js', 'utf8'); + fs.writeFileSync('svelte.config.js', remove_package_from_config(content)); + } catch { + console.log( + colors + .bold() + .yellow('Could not remove package config from svelte.config.js, please remove it manually') + ); + } +} + +/** + * @param {string} content + */ +export function remove_package_from_config(content) { + const ast = ts.createSourceFile( + 'filename.ts', + content, + ts.ScriptTarget.Latest, + true, + ts.ScriptKind.TS + ); + + const code = new MagicString(content); + + for (const statement of ast.statements) { + if (ts.isExportAssignment(statement)) { + if (ts.isObjectLiteralExpression(statement.expression)) { + remove(statement.expression); + } else if (ts.isIdentifier(statement.expression)) { + for (const statement2 of ast.statements) { + if (ts.isVariableStatement(statement2)) { + for (const declaration of statement2.declarationList.declarations) { + if ( + ts.isIdentifier(declaration.name) && + declaration.name.text === statement.expression.text && + declaration.initializer && + ts.isObjectLiteralExpression(declaration.initializer) + ) { + remove(declaration.initializer); + } + } + } + } + } + } + } + + return code.toString(); + + /** @param {ts.ObjectLiteralExpression} expression */ + function remove(expression) { + for (let i = 0; i < expression.properties.length; i++) { + const property = expression.properties[i]; + if ( + ts.isPropertyAssignment(property) && + ts.isIdentifier(property.name) && + property.name.text === 'package' && + ts.isObjectLiteralExpression(property.initializer) + ) { + if (expression.properties.length === 1) { + code.overwrite(expression.getStart(), expression.getEnd(), '{}'); + } else { + const next_property = expression.properties[i + 1]; + if (next_property) { + code.remove(property.getStart(), next_property.getStart()); + } else { + code.remove(property.getStart(), content.lastIndexOf('}', expression.getEnd())); + } + } + } + } + } +} diff --git a/packages/migrate/migrations/package/migrate_config.spec.js b/packages/migrate/migrations/package/migrate_config.spec.js new file mode 100644 index 000000000000..e640d1db1d2c --- /dev/null +++ b/packages/migrate/migrations/package/migrate_config.spec.js @@ -0,0 +1,81 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { remove_package_from_config } from './migrate_config.js'; + +test('Removes package config #1', () => { + const result = remove_package_from_config(` + export default { + kit: { + files: { + lib: 'src/lib' + } + }, + package: { + dir: 'package', + exports: (filepath) => !/^_|\/_|\.d\.ts$/.test(filepath), + files: () => true + }, + preprocess: [] + }`); + assert.equal( + result, + ` + export default { + kit: { + files: { + lib: 'src/lib' + } + }, + preprocess: [] + }` + ); +}); + +test('Removes package config #2', () => { + const result = remove_package_from_config(` + export default { + package: { + dir: 'package', + exports: (filepath) => !/^_|\/_|\.d\.ts$/.test(filepath), + files: () => true + }, + }`); + assert.equal( + result, + ` + export default {}` + ); +}); + +test('Removes package config #3', () => { + const result = remove_package_from_config(` + const config = { + package: { + dir: 'package', + exports: (filepath) => !/^_|\/_|\.d\.ts$/.test(filepath), + files: () => true + }, + }; + export default config;`); + assert.equal( + result, + ` + const config = {}; + export default config;` + ); +}); + +test('Leaves config untouched', () => { + const content = ` + export default { + kit: { + files: { + lib: 'src/lib' + } + }, + }`; + const result = remove_package_from_config(content); + assert.equal(result, content); +}); + +test.run(); diff --git a/packages/migrate/migrations/package/migrate_pkg.js b/packages/migrate/migrations/package/migrate_pkg.js new file mode 100644 index 000000000000..044bb71dcf24 --- /dev/null +++ b/packages/migrate/migrations/package/migrate_pkg.js @@ -0,0 +1,160 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import colors from 'kleur'; +import { guess_indent, posixify, walk } from '../../utils.js'; + +/** + * @param {any} config + */ +export function migrate_pkg(config) { + const files = scan(config); + const pkg_str = fs.readFileSync('package.json', 'utf8'); + const pkg = update_pkg_json(config, JSON.parse(pkg_str), files); + fs.writeFileSync('package.json', JSON.stringify(pkg, null, guess_indent(pkg_str) ?? '\t')); +} + +/** + * @param {any} config + */ +function scan(config) { + return walk(config.package.source).map((file) => analyze(config, file)); +} + +/** + * @param {any} config + * @param {string} file + */ +function analyze(config, file) { + const name = posixify(file); + + const svelte_extension = config.extensions.find((/** @type {string} */ ext) => + name.endsWith(ext) + ); + + const dest = svelte_extension + ? name.slice(0, -svelte_extension.length) + '.svelte' + : name.endsWith('.d.ts') + ? name + : name.endsWith('.ts') + ? name.slice(0, -3) + '.js' + : name; + + return { + name, + dest, + is_included: config.package.files(name), + is_exported: config.package.exports(name), + is_svelte: !!svelte_extension + }; +} + +/** + * @param {any} config + * @param {any} pkg + * @param {ReturnType[]} files + */ +export function update_pkg_json(config, pkg, files) { + const out_dir = path.relative('.', config.package.dir); + + // See: https://pnpm.io/package_json#publishconfigdirectory + if (pkg.publishConfig?.directory || pkg.linkDirectory?.directory) { + console.log( + colors.yellow( + `Detected "publishConfig.directory" or "linkDirectory.directory" fields in your package.json. ` + + `This migration removes them, which may or may not be what you want. Please review closely.` + ) + ); + delete pkg.publishConfig?.directory; + delete pkg.linkDirectory?.directory; + } + + for (const key in pkg.scripts || []) { + const script = pkg.scripts[key]; + if (script.includes('svelte-package')) { + pkg.scripts[key] = script.replace('svelte-package', `svelte-package -o ${out_dir}`); + } + } + + pkg.type = 'module'; + pkg.exports = { + './package.json': './package.json', + ...pkg.exports + }; + + pkg.files = pkg.files || []; + if (!pkg.files.includes(out_dir)) { + pkg.files.push(out_dir); + } + + if (pkg.devDependencies?.['@sveltejs/package']) { + pkg.devDependencies['@sveltejs/package'] = '^2.0.0'; + } + + /** @type {Record} */ + const clashes = {}; + + for (const file of files) { + if (file.is_included && file.is_exported) { + const original = `$lib/${file.name}`; + const key = `./${file.dest}`.replace(/\/index\.js$|(\/[^/]+)\.js$/, '$1'); + + if (clashes[key]) { + console.log( + colors.yellow( + `Duplicate "${key}" export. Closely review your "exports" field in package.json after the migration.` + ) + ); + } + + if (!pkg.exports[key]) { + const has_type = config.package.emitTypes && (file.is_svelte || file.dest.endsWith('.js')); + const needs_svelte_condition = file.is_svelte || path.basename(file.dest) === 'index.js'; + // JSON.stringify will remove the undefined entries + pkg.exports[key] = { + types: has_type + ? `./${out_dir}/${ + file.is_svelte ? `${file.dest}.d.ts` : file.dest.slice(0, -'.js'.length) + '.d.ts' + }` + : undefined, + svelte: needs_svelte_condition ? `./${out_dir}/${file.dest}` : undefined, + default: `./${out_dir}/${file.dest}` + }; + + if (Object.values(pkg.exports[key]).filter(Boolean).length === 1) { + pkg.exports[key] = pkg.exports[key].default; + } + } + + clashes[key] = original; + } + } + + if (!pkg.svelte && files.some((file) => file.is_svelte)) { + // Several heuristics in Kit/vite-plugin-svelte to tell Vite to mark Svelte packages + // rely on the "svelte" property. Vite/Rollup/Webpack plugin can all deal with it. + // See https://github.com/sveltejs/kit/issues/1959 for more info and related threads. + if (pkg.exports['.']) { + const svelte_export = + typeof pkg.exports['.'] === 'string' + ? pkg.exports['.'] + : pkg.exports['.'].svelte || pkg.exports['.'].import || pkg.exports['.'].default; + if (svelte_export) { + pkg.svelte = svelte_export; + } else { + console.log( + colors.yellow( + 'Cannot generate a "svelte" entry point because the "." entry in "exports" is not a string. Please specify a "svelte" entry point yourself\n' + ) + ); + } + } else { + console.log( + colors.yellow( + 'Cannot generate a "svelte" entry point because the "." entry in "exports" is missing. Please specify a "svelte" entry point yourself\n' + ) + ); + } + } + + return pkg; +} diff --git a/packages/migrate/migrations/package/migrate_pkg.spec.js b/packages/migrate/migrations/package/migrate_pkg.spec.js new file mode 100644 index 000000000000..44fce618f2fc --- /dev/null +++ b/packages/migrate/migrations/package/migrate_pkg.spec.js @@ -0,0 +1,138 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { update_pkg_json } from './migrate_pkg.js'; + +test('Updates package.json', () => { + const result = update_pkg_json( + { package: { dir: 'package', emitTypes: true } }, + { + name: 'foo', + version: '1.0.0', + scripts: { + packages: 'svelte-package' + }, + exports: { + './ignored': './something.js' + } + }, + [ + { + name: 'foo/Bar.svelte', + dest: 'foo/Bar.svelte', + is_exported: true, + is_included: true, + is_svelte: true + }, + { + name: 'foo/Bar2.svelte', + dest: 'foo/Bar2.svelte', + is_exported: false, + is_included: false, + is_svelte: true + }, + { + name: 'baz.js', + dest: 'baz.js', + is_exported: true, + is_included: true, + is_svelte: false + }, + { + name: 'index.js', + dest: 'index.js', + is_exported: true, + is_included: true, + is_svelte: false + }, + { + name: 'ignored.js', + dest: 'ignored.js', + is_exported: true, + is_included: true, + is_svelte: false + } + ] + ); + assert.equal(JSON.parse(JSON.stringify(result)), { + name: 'foo', + version: '1.0.0', + type: 'module', + files: ['package'], + scripts: { + packages: 'svelte-package -o package' + }, + exports: { + './package.json': './package.json', + '.': { + types: './package/index.d.ts', + svelte: './package/index.js', + default: './package/index.js' + }, + './foo/Bar.svelte': { + types: './package/foo/Bar.svelte.d.ts', + svelte: './package/foo/Bar.svelte', + default: './package/foo/Bar.svelte' + }, + './baz': { + types: './package/baz.d.ts', + default: './package/baz.js' + }, + './ignored': './something.js' + }, + svelte: './package/index.js' + }); +}); + +test('Updates package.json #2', () => { + const result = update_pkg_json( + { package: { dir: 'dist', emitTypes: false } }, + { + name: 'foo', + version: '1.0.0' + }, + [ + { + name: 'foo/Bar.svelte', + dest: 'foo/Bar.svelte', + is_exported: true, + is_included: true, + is_svelte: true + }, + { + name: 'baz.js', + dest: 'baz.js', + is_exported: true, + is_included: true, + is_svelte: false + }, + { + name: 'index.js', + dest: 'index.js', + is_exported: true, + is_included: true, + is_svelte: false + } + ] + ); + assert.equal(JSON.parse(JSON.stringify(result)), { + name: 'foo', + version: '1.0.0', + type: 'module', + files: ['dist'], + exports: { + './package.json': './package.json', + '.': { + svelte: './dist/index.js', + default: './dist/index.js' + }, + './foo/Bar.svelte': { + svelte: './dist/foo/Bar.svelte', + default: './dist/foo/Bar.svelte' + }, + './baz': './dist/baz.js' + }, + svelte: './dist/index.js' + }); +}); + +test.run(); diff --git a/packages/migrate/migrations/routes/index.js b/packages/migrate/migrations/routes/index.js index 14b55a393691..a6568e5312c6 100644 --- a/packages/migrate/migrations/routes/index.js +++ b/packages/migrate/migrations/routes/index.js @@ -1,4 +1,3 @@ -import { execSync } from 'child_process'; import fs from 'fs'; import colors from 'kleur'; import path from 'path'; @@ -9,7 +8,8 @@ import { migrate_scripts } from './migrate_scripts/index.js'; import { migrate_page } from './migrate_page_js/index.js'; import { migrate_page_server } from './migrate_page_server/index.js'; import { migrate_server } from './migrate_server/index.js'; -import { adjust_imports, bail, move_file, relative, task } from './utils.js'; +import { adjust_imports, task } from './utils.js'; +import { bail, relative, move_file, check_git } from '../../utils.js'; export async function migrate() { if (!fs.existsSync('svelte.config.js')) { @@ -57,34 +57,7 @@ export async function migrate() { console.log(colors.bold().yellow('\nThis will overwrite files in the current directory!\n')); - let use_git = false; - - let dir = process.cwd(); - do { - if (fs.existsSync(path.join(dir, '.git'))) { - use_git = true; - break; - } - } while (dir !== (dir = path.dirname(dir))); - - if (use_git) { - try { - const status = execSync('git status --porcelain', { stdio: 'pipe' }).toString(); - - if (status) { - const message = - 'Your git working directory is dirty — we recommend committing your changes before running this migration.\n'; - console.log(colors.bold().red(message)); - } - } catch { - // would be weird to have a .git folder if git is not installed, - // but always expect the unexpected - const message = - 'Could not detect a git installation. If this is unexpected, please raise an issue: https://github.com/sveltejs/kit.\n'; - console.log(colors.bold().red(message)); - use_git = false; - } - } + const use_git = check_git(); const response = await prompts({ type: 'confirm', diff --git a/packages/migrate/migrations/routes/migrate_page_js/index.js b/packages/migrate/migrations/routes/migrate_page_js/index.js index a4947f475378..1a2b30ac83f5 100644 --- a/packages/migrate/migrations/routes/migrate_page_js/index.js +++ b/packages/migrate/migrations/routes/migrate_page_js/index.js @@ -1,7 +1,6 @@ import ts from 'typescript'; import { automigration, - dedent, error, get_function_node, get_object_nodes, @@ -15,6 +14,7 @@ import { unwrap } from '../utils.js'; import * as TASKS from '../tasks.js'; +import { dedent } from '../../../utils.js'; const give_up = `${error('Update load function', TASKS.PAGE_LOAD)}\n\n`; diff --git a/packages/migrate/migrations/routes/migrate_page_server/index.js b/packages/migrate/migrations/routes/migrate_page_server/index.js index c5530f05949e..8475f14ba9cd 100644 --- a/packages/migrate/migrations/routes/migrate_page_server/index.js +++ b/packages/migrate/migrations/routes/migrate_page_server/index.js @@ -1,7 +1,6 @@ import ts from 'typescript'; import { automigration, - dedent, error, get_function_node, get_object_nodes, @@ -14,6 +13,7 @@ import { uppercase_migration } from '../utils.js'; import * as TASKS from '../tasks.js'; +import { dedent } from '../../../utils.js'; const give_up = `${error('Update +page.server.js', TASKS.PAGE_ENDPOINT)}\n\n`; diff --git a/packages/migrate/migrations/routes/migrate_scripts/index.js b/packages/migrate/migrations/routes/migrate_scripts/index.js index 2950dae5df60..5ecbbe8cd766 100644 --- a/packages/migrate/migrations/routes/migrate_scripts/index.js +++ b/packages/migrate/migrations/routes/migrate_scripts/index.js @@ -1,14 +1,7 @@ import ts from 'typescript'; -import { - adjust_imports, - guess_indent, - comment, - error, - dedent, - parse, - except_str -} from '../utils.js'; +import { adjust_imports, error, parse } from '../utils.js'; import * as TASKS from '../tasks.js'; +import { comment, dedent, except_str, guess_indent } from '../../../utils.js'; /** * @param {string} content diff --git a/packages/migrate/migrations/routes/migrate_server/index.js b/packages/migrate/migrations/routes/migrate_server/index.js index 71ab5ab55894..af3653db1c75 100644 --- a/packages/migrate/migrations/routes/migrate_server/index.js +++ b/packages/migrate/migrations/routes/migrate_server/index.js @@ -1,13 +1,10 @@ import ts from 'typescript'; import { automigration, - dedent, uppercase_migration, error, get_function_node, get_object_nodes, - guess_indent, - indent_at_line, is_new, is_string_like, manual_return_migration, @@ -16,6 +13,7 @@ import { unwrap } from '../utils.js'; import * as TASKS from '../tasks.js'; +import { dedent, guess_indent, indent_at_line } from '../../../utils.js'; const give_up = `${error('Update +server.js', TASKS.STANDALONE_ENDPOINT)}\n\n`; diff --git a/packages/migrate/migrations/routes/utils.js b/packages/migrate/migrations/routes/utils.js index 708550113fb0..6bfc40f01a11 100644 --- a/packages/migrate/migrations/routes/utils.js +++ b/packages/migrate/migrations/routes/utils.js @@ -1,20 +1,7 @@ import fs from 'fs'; -import path from 'path'; -import colors from 'kleur'; import ts from 'typescript'; import MagicString from 'magic-string'; -import { execFileSync } from 'child_process'; - -/** @param {string} message */ -export function bail(message) { - console.error(colors.bold().red(message)); - process.exit(1); -} - -/** @param {string} file */ -export function relative(file) { - return path.relative('.', file); -} +import { comment, indent_at_line } from '../../utils.js'; /** * @param {string} description @@ -37,31 +24,6 @@ export function error(description, comment_id) { return `throw new Error(${JSON.stringify(task(description, comment_id))});`; } -/** - * - * @param {string} file - * @param {string} renamed - * @param {string} content - * @param {boolean} use_git - */ -export function move_file(file, renamed, content, use_git) { - if (use_git) { - execFileSync('git', ['mv', file, renamed]); - } else { - fs.unlinkSync(file); - } - - fs.writeFileSync(renamed, content); -} - -/** - * @param {string} contents - * @param {string} indent - */ -export function comment(contents, indent) { - return contents.replace(new RegExp(`^${indent}`, 'gm'), `${indent}// `); -} - /** @param {string} content */ export function adjust_imports(content) { try { @@ -115,86 +77,6 @@ export function adjust_imports(content) { } } -/** @param {string} content */ -export function dedent(content) { - const indent = guess_indent(content); - if (!indent) return content; - - /** @type {string[]} */ - const substitutions = []; - - try { - const ast = ts.createSourceFile( - 'filename.ts', - content, - ts.ScriptTarget.Latest, - true, - ts.ScriptKind.TS - ); - - const code = new MagicString(content); - - /** @param {ts.Node} node */ - function walk(node) { - if (ts.isTemplateLiteral(node)) { - let pos = node.pos; - while (/\s/.test(content[pos])) pos += 1; - - code.overwrite(pos, node.end, `____SUBSTITUTION_${substitutions.length}____`); - substitutions.push(node.getText()); - } - - node.forEachChild(walk); - } - - ast.forEachChild(walk); - - return code - .toString() - .replace(new RegExp(`^${indent}`, 'gm'), '') - .replace(/____SUBSTITUTION_(\d+)____/g, (match, index) => substitutions[index]); - } catch { - // as above — ignore this edge case - return content; - } -} - -/** @param {string} content */ -export function guess_indent(content) { - const lines = content.split('\n'); - - const tabbed = lines.filter((line) => /^\t+/.test(line)); - const spaced = lines.filter((line) => /^ {2,}/.test(line)); - - if (tabbed.length === 0 && spaced.length === 0) { - return null; - } - - // More lines tabbed than spaced? Assume tabs, and - // default to tabs in the case of a tie (or nothing - // to go on) - if (tabbed.length >= spaced.length) { - return '\t'; - } - - // Otherwise, we need to guess the multiple - const min = spaced.reduce((previous, current) => { - const count = /^ +/.exec(current)?.[0].length ?? 0; - return Math.min(count, previous); - }, Infinity); - - return ' '.repeat(min); -} - -/** - * @param {string} content - * @param {number} offset - */ -export function indent_at_line(content, offset) { - const substr = content.substring(content.lastIndexOf('\n', offset) + 1, offset); - return /\s*/.exec(substr)?.[0] ?? ''; -} - /** * * @param {ts.Node} node @@ -426,16 +308,6 @@ export function parse(content) { } } -/** - * @param {string} content - * @param {string} except - */ -export function except_str(content, except) { - const start = content.indexOf(except); - const end = start + except.length; - return content.substring(0, start) + content.substring(end); -} - /** @param {string} test_file */ export function read_samples(test_file) { const markdown = fs.readFileSync(new URL('./samples.md', test_file), 'utf8'); diff --git a/packages/migrate/tsconfig.json b/packages/migrate/tsconfig.json index 07a6cc58c168..1bd4194a49b1 100644 --- a/packages/migrate/tsconfig.json +++ b/packages/migrate/tsconfig.json @@ -9,5 +9,5 @@ "moduleResolution": "node", "allowSyntheticDefaultImports": true }, - "include": ["./migrations/**/*", "./bin.js"] + "include": ["./migrations/**/*", "./utils.js", "./bin.js"] } diff --git a/packages/migrate/utils.js b/packages/migrate/utils.js new file mode 100644 index 000000000000..1e03d9fed30d --- /dev/null +++ b/packages/migrate/utils.js @@ -0,0 +1,200 @@ +import fs from 'fs'; +import path from 'path'; +import colors from 'kleur'; +import ts from 'typescript'; +import MagicString from 'magic-string'; +import { execFileSync, execSync } from 'node:child_process'; + +/** @param {string} message */ +export function bail(message) { + console.error(colors.bold().red(message)); + process.exit(1); +} + +/** @param {string} file */ +export function relative(file) { + return path.relative('.', file); +} +/** + * + * @param {string} file + * @param {string} renamed + * @param {string} content + * @param {boolean} use_git + */ +export function move_file(file, renamed, content, use_git) { + if (use_git) { + execFileSync('git', ['mv', file, renamed]); + } else { + fs.unlinkSync(file); + } + + fs.writeFileSync(renamed, content); +} + +/** + * @param {string} contents + * @param {string} indent + */ +export function comment(contents, indent) { + return contents.replace(new RegExp(`^${indent}`, 'gm'), `${indent}// `); +} + +/** @param {string} content */ +export function dedent(content) { + const indent = guess_indent(content); + if (!indent) return content; + + /** @type {string[]} */ + const substitutions = []; + + try { + const ast = ts.createSourceFile( + 'filename.ts', + content, + ts.ScriptTarget.Latest, + true, + ts.ScriptKind.TS + ); + + const code = new MagicString(content); + + /** @param {ts.Node} node */ + function walk(node) { + if (ts.isTemplateLiteral(node)) { + let pos = node.pos; + while (/\s/.test(content[pos])) pos += 1; + + code.overwrite(pos, node.end, `____SUBSTITUTION_${substitutions.length}____`); + substitutions.push(node.getText()); + } + + node.forEachChild(walk); + } + + ast.forEachChild(walk); + + return code + .toString() + .replace(new RegExp(`^${indent}`, 'gm'), '') + .replace(/____SUBSTITUTION_(\d+)____/g, (match, index) => substitutions[index]); + } catch { + // as above — ignore this edge case + return content; + } +} + +/** @param {string} content */ +export function guess_indent(content) { + const lines = content.split('\n'); + + const tabbed = lines.filter((line) => /^\t+/.test(line)); + const spaced = lines.filter((line) => /^ {2,}/.test(line)); + + if (tabbed.length === 0 && spaced.length === 0) { + return null; + } + + // More lines tabbed than spaced? Assume tabs, and + // default to tabs in the case of a tie (or nothing + // to go on) + if (tabbed.length >= spaced.length) { + return '\t'; + } + + // Otherwise, we need to guess the multiple + const min = spaced.reduce((previous, current) => { + const count = /^ +/.exec(current)?.[0].length ?? 0; + return Math.min(count, previous); + }, Infinity); + + return ' '.repeat(min); +} + +/** + * @param {string} content + * @param {number} offset + */ +export function indent_at_line(content, offset) { + const substr = content.substring(content.lastIndexOf('\n', offset) + 1, offset); + return /\s*/.exec(substr)?.[0] ?? ''; +} + +/** + * @param {string} content + * @param {string} except + */ +export function except_str(content, except) { + const start = content.indexOf(except); + const end = start + except.length; + return content.substring(0, start) + content.substring(end); +} + +/** + * @returns {boolean} True if git is installed + */ +export function check_git() { + let use_git = false; + + let dir = process.cwd(); + do { + if (fs.existsSync(path.join(dir, '.git'))) { + use_git = true; + break; + } + } while (dir !== (dir = path.dirname(dir))); + + if (use_git) { + try { + const status = execSync('git status --porcelain', { stdio: 'pipe' }).toString(); + + if (status) { + const message = + 'Your git working directory is dirty — we recommend committing your changes before running this migration.\n'; + console.log(colors.bold().red(message)); + } + } catch { + // would be weird to have a .git folder if git is not installed, + // but always expect the unexpected + const message = + 'Could not detect a git installation. If this is unexpected, please raise an issue: https://github.com/sveltejs/kit.\n'; + console.log(colors.bold().red(message)); + use_git = false; + } + } + + return use_git; +} + +/** + * Get a list of all files in a directory + * @param {string} cwd - the directory to walk + * @param {boolean} [dirs] - whether to include directories in the result + */ +export function walk(cwd, dirs = false) { + /** @type {string[]} */ + const all_files = []; + + /** @param {string} dir */ + function walk_dir(dir) { + const files = fs.readdirSync(path.join(cwd, dir)); + + for (const file of files) { + const joined = path.join(dir, file); + const stats = fs.statSync(path.join(cwd, joined)); + if (stats.isDirectory()) { + if (dirs) all_files.push(joined); + walk_dir(joined); + } else { + all_files.push(joined); + } + } + } + + return walk_dir(''), all_files; +} + +/** @param {string} str */ +export function posixify(str) { + return str.replace(/\\/g, '/'); +} diff --git a/packages/package/src/cli.js b/packages/package/src/cli.js index bb33f5058a82..bee0fc0d8bbd 100644 --- a/packages/package/src/cli.js +++ b/packages/package/src/cli.js @@ -18,13 +18,33 @@ const prog = sade('svelte-package', true).version('__VERSION__'); prog .describe('Create a package') + .option('-i, --input', 'Input directory') + .option('-o, --output', 'Output directory', 'dist') + .option('-t, --types', 'Emit type declarations', true) .option('-w, --watch', 'Rerun when files change', false) - .action(async ({ watch }) => { + .action(async (args) => { try { const config = await load_config(); + + // @ts-expect-error + if (config.package) { + throw new Error( + `config.package is no longer supported. See https://github.com/sveltejs/kit/pull/8922 for more information and how to migrate.` + ); + } + const packaging = await import('./index.js'); - await (watch ? packaging.watch(config) : packaging.build(config)); + /** @type {import('./types').Options} */ + const options = { + cwd: process.cwd(), + input: args.input ?? config.kit?.files?.lib ?? 'src/lib', + output: args.output, + types: args.types, + config + }; + + await (args.watch ? packaging.watch(options) : packaging.build(options)); } catch (error) { handle_error(/** @type {Error} */ (error)); } diff --git a/packages/package/src/config.js b/packages/package/src/config.js index b094dee11e06..a0433d355396 100644 --- a/packages/package/src/config.js +++ b/packages/package/src/config.js @@ -1,39 +1,27 @@ -import path from 'path'; -import fs from 'fs'; -import url from 'url'; +import path from 'node:path'; +import fs from 'node:fs'; +import url from 'node:url'; /** * Loads and validates svelte.config.js * @param {{ cwd?: string }} options - * @returns {Promise} + * @returns {Promise} */ export async function load_config({ cwd = process.cwd() } = {}) { const config_file = path.join(cwd, 'svelte.config.js'); if (!fs.existsSync(config_file)) { - return process_config({}, { cwd }); + return {}; } - const config = await import(`${url.pathToFileURL(config_file).href}?ts=${Date.now()}`); + const module = await import(`${url.pathToFileURL(config_file).href}?ts=${Date.now()}`); + const config = module.default; - return process_config(config.default, { cwd }); -} + if (config.package) { + throw new Error( + `config.package is no longer supported. See https://github.com/sveltejs/kit/discussions/8825 for more information.` + ); + } -/** - * @param {import('types').Config} config - * @returns {import('./types').ValidatedConfig} - */ -function process_config(config, { cwd = process.cwd() } = {}) { - return { - extensions: config.extensions ?? ['.svelte'], - kit: config.kit, - package: { - source: path.resolve(cwd, config.kit?.files?.lib ?? config.package?.source ?? 'src/lib'), - dir: config.package?.dir ?? 'package', - exports: config.package?.exports ?? ((filepath) => !/^_|\/_|\.d\.ts$/.test(filepath)), - files: config.package?.files ?? (() => true), - emitTypes: config.package?.emitTypes ?? true - }, - preprocess: config.preprocess - }; + return config; } diff --git a/packages/package/src/index.js b/packages/package/src/index.js index e7af57df8140..fb8690934f7a 100644 --- a/packages/package/src/index.js +++ b/packages/package/src/index.js @@ -1,105 +1,68 @@ -import * as fs from 'fs'; -import { dirname, join, relative } from 'path'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; import colors from 'kleur'; import chokidar from 'chokidar'; import { preprocess } from 'svelte/compiler'; import { copy, mkdirp, rimraf } from './filesystem.js'; -import { analyze, generate_pkg, resolve_lib_alias, scan, strip_lang_tags, write } from './utils.js'; +import { analyze, resolve_aliases, scan, strip_lang_tags, write } from './utils.js'; import { emit_dts, transpile_ts } from './typescript.js'; +import { create_validator } from './validate.js'; -const essential_files = ['README', 'LICENSE', 'CHANGELOG', '.gitignore', '.npmignore']; +/** + * @param {import('./types').Options} options + */ +export async function build(options) { + const { analyse_code, validate } = create_validator(options); + await do_build(options, analyse_code); + validate(); +} /** - * @param {import('./types').ValidatedConfig} config - * @param {string} cwd + * @param {import('./types').Options} options + * @param {(name: string, code: string) => void} analyse_code */ -export async function build(config, cwd = process.cwd()) { - const { source: lib } = config.package; - const { dir } = config.package; +async function do_build(options, analyse_code) { + const { input, output, extensions, alias } = normalize_options(options); - if (!fs.existsSync(lib)) { - throw new Error(`${lib} does not exist`); + if (!fs.existsSync(input)) { + throw new Error(`${path.relative('.', input)} does not exist`); } - rimraf(dir); - mkdirp(dir); + rimraf(output); + mkdirp(output); - const files = scan(config); + const files = scan(input, extensions); - if (config.package.emitTypes) { - await emit_dts(config, cwd, files); + if (options.types) { + await emit_dts(input, output, options.cwd, alias, files); } - const pkg = generate_pkg(cwd, files); - - if (!pkg.dependencies?.svelte && !pkg.peerDependencies?.svelte) { - console.warn( - 'Svelte libraries should include "svelte" in either "dependencies" or "peerDependencies".' - ); - } - - if (!pkg.svelte && files.some((file) => file.is_svelte)) { - // Several heuristics in Kit/vite-plugin-svelte to tell Vite to mark Svelte packages - // rely on the "svelte" property. Vite/Rollup/Webpack plugin can all deal with it. - // See https://github.com/sveltejs/kit/issues/1959 for more info and related threads. - if (pkg.exports['.']) { - const svelte_export = - typeof pkg.exports['.'] === 'string' - ? pkg.exports['.'] - : pkg.exports['.'].import || pkg.exports['.'].default; - if (svelte_export) { - pkg.svelte = svelte_export; - } else { - console.warn( - 'Cannot generate a "svelte" entry point because the "." entry in "exports" is not a string. If you set it by hand, please also set one of the options as a "svelte" entry point\n' - ); - } - } else { - console.warn( - 'Cannot generate a "svelte" entry point because the "." entry in "exports" is missing. Please specify one or set a "svelte" entry point yourself\n' - ); - } - } - - write(join(dir, 'package.json'), JSON.stringify(pkg, null, 2)); - for (const file of files) { - await process_file(config, file); - } - - const whitelist = fs.readdirSync(cwd).filter((file) => { - const lowercased = file.toLowerCase(); - return essential_files.some((name) => lowercased.startsWith(name.toLowerCase())); - }); - - for (const pathname of whitelist) { - const full_path = join(cwd, pathname); - if (fs.lstatSync(full_path).isDirectory()) continue; // just to be sure - - const package_path = join(dir, pathname); - if (!fs.existsSync(package_path)) fs.copyFileSync(full_path, package_path); + await process_file(input, output, file, options.config.preprocess, alias, analyse_code); } - const from = relative(cwd, lib); - const to = relative(cwd, dir); - console.log(colors.bold().green(`${from} -> ${to}`)); - console.log(`Successfully built '${pkg.name}' package. To publish it to npm:`); - console.log(colors.bold().cyan(` cd ${to}`)); - console.log(colors.bold().cyan(' npm publish\n')); + console.log( + colors + .bold() + .green(`${path.relative(options.cwd, input)} -> ${path.relative(options.cwd, output)}`) + ); } /** - * @param {import('./types').ValidatedConfig} config + * @param {import('./types').Options} options */ -export async function watch(config, cwd = process.cwd()) { - await build(config, cwd); +export async function watch(options) { + const { analyse_code, validate } = create_validator(options); - const message = `\nWatching ${relative(cwd, config.package.source)} for changes...\n`; + await do_build(options, analyse_code); - console.log(message); + validate(); - const { source: lib } = config.package; - const { dir } = config.package; + const { input, output, extensions, alias } = normalize_options(options); + + const message = `\nWatching ${path.relative(options.cwd, input)} for changes...\n`; + + console.log(message); /** @type {Array<{ file: import('./types').File, type: string }>} */ const pending = []; @@ -110,29 +73,22 @@ export async function watch(config, cwd = process.cwd()) { /** @type {NodeJS.Timeout} */ let timeout; - const watcher = chokidar.watch(lib, { ignoreInitial: true }); + const watcher = chokidar.watch(input, { ignoreInitial: true }); const ready = new Promise((resolve) => watcher.on('ready', resolve)); - watcher.on('all', async (type, path) => { - const file = analyze(config, relative(lib, path)); - if (!file.is_included) return; + watcher.on('all', async (type, filepath) => { + const file = analyze(path.relative(input, filepath), extensions); pending.push({ file, type }); clearTimeout(timeout); timeout = setTimeout(async () => { - const files = scan(config); - - let should_update_pkg = false; + const files = scan(input, extensions); const events = pending.slice(); pending.length = 0; for (const { file, type } of events) { - if ((type === 'unlink' || type === 'add') && file.is_exported) { - should_update_pkg = true; - } - if (type === 'unlink') { for (const candidate of [ file.name, @@ -140,13 +96,13 @@ export async function watch(config, cwd = process.cwd()) { `${file.base}.d.mts`, `${file.base}.d.cts` ]) { - const resolved = join(dir, candidate); + const resolved = path.join(output, candidate); if (fs.existsSync(resolved)) { fs.unlinkSync(resolved); - const parent = dirname(resolved); - if (parent !== dir && fs.readdirSync(parent).length === 0) { + const parent = path.dirname(resolved); + if (parent !== output && fs.readdirSync(parent).length === 0) { fs.rmdirSync(parent); } } @@ -155,19 +111,14 @@ export async function watch(config, cwd = process.cwd()) { } if (type === 'add' || type === 'change') { - await process_file(config, file); console.log(`Processing ${file.name}`); + await process_file(input, output, file, options.config.preprocess, alias, analyse_code); + validate(); } } - if (should_update_pkg) { - const pkg = generate_pkg(cwd, files); - write(join(dir, 'package.json'), JSON.stringify(pkg, null, 2)); - console.log('Updated package.json'); - } - - if (config.package.emitTypes) { - await emit_dts(config, cwd, scan(config)); + if (options.types) { + await emit_dts(input, output, options.cwd, alias, files); console.log('Updated .d.ts files'); } @@ -189,21 +140,44 @@ export async function watch(config, cwd = process.cwd()) { } /** - * @param {import('./types').ValidatedConfig} config - * @param {import('./types').File} file + * @param {import('./types').Options} options */ -async function process_file(config, file) { - if (!file.is_included) return; +function normalize_options(options) { + const input = path.resolve(options.cwd, options.input); + const output = path.resolve(options.cwd, options.output); + const extensions = options.config.extensions ?? ['.svelte']; + + const alias = { + $lib: path.resolve(options.cwd, options.config.kit?.files?.lib ?? 'src/lib'), + ...(options.config.kit?.alias ?? {}) + }; + + return { + input, + output, + extensions, + alias + }; +} - const filename = join(config.package.source, file.name); - const dest = join(config.package.dir, file.dest); +/** + * @param {string} input + * @param {string} output + * @param {import('./types').File} file + * @param {import('svelte/types/compiler/preprocess').PreprocessorGroup | undefined} preprocessor + * @param {Record} aliases + * @param {(name: string, code: string) => void} analyse_code + */ +async function process_file(input, output, file, preprocessor, aliases, analyse_code) { + const filename = path.join(input, file.name); + const dest = path.join(output, file.dest); - if (file.is_svelte || file.name.endsWith('.ts')) { + if (file.is_svelte || file.name.endsWith('.ts') || file.name.endsWith('.js')) { let contents = fs.readFileSync(filename, 'utf-8'); if (file.is_svelte) { - if (config.preprocess) { - const preprocessed = (await preprocess(contents, config.preprocess, { filename })).code; + if (preprocessor) { + const preprocessed = (await preprocess(contents, preprocessor, { filename })).code; contents = strip_lang_tags(preprocessed); } } @@ -212,7 +186,8 @@ async function process_file(config, file) { contents = await transpile_ts(filename, contents); } - contents = resolve_lib_alias(file.name, contents, config); + contents = resolve_aliases(input, file.name, contents, aliases); + analyse_code(file.name, contents); write(dest, contents); } else { copy(filename, dest); diff --git a/packages/package/src/types.d.ts b/packages/package/src/types.d.ts index 2350e43f3b71..3e481ed2bef9 100644 --- a/packages/package/src/types.d.ts +++ b/packages/package/src/types.d.ts @@ -1,11 +1,26 @@ -import type { PackageConfig } from 'types'; +import { PreprocessorGroup } from 'svelte/types/compiler/preprocess'; + +export interface Options { + cwd: string; + input: string; + output: string; + types: boolean; + config: { + extensions?: string[]; + kit?: { + alias?: Record; + files?: { + lib?: string; + }; + }; + preprocess?: PreprocessorGroup; + }; +} export interface File { name: string; dest: string; base: string; - is_included: boolean; - is_exported: boolean; is_svelte: boolean; } @@ -20,7 +35,6 @@ export type RecursiveRequired = { export interface ValidatedConfig { extensions: string[]; kit?: any; - package: RecursiveRequired; preprocess?: any; } diff --git a/packages/package/src/typescript.js b/packages/package/src/typescript.js index fa75d51c73d6..db91a6529724 100644 --- a/packages/package/src/typescript.js +++ b/packages/package/src/typescript.js @@ -2,7 +2,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { createRequire } from 'module'; import { posixify, mkdirp, rimraf, walk } from './filesystem.js'; -import { resolve_lib_alias, write } from './utils.js'; +import { resolve_aliases, write } from './utils.js'; import { emitDts } from 'svelte2tsx'; /** @@ -10,18 +10,20 @@ import { emitDts } from 'svelte2tsx'; * The files are written to a temporary location and those which should be kept * are sanitized ($lib alias resolved) and copied over to the destination folder. * - * @param {import('./types').ValidatedConfig} config + * @param {string} input + * @param {string} output * @param {string} cwd + * @param {Record} alias * @param {import('./types').File[]} files */ -export async function emit_dts(config, cwd, files) { - const tmp = `${config.package.dir}/__package_types_tmp__`; +export async function emit_dts(input, output, cwd, alias, files) { + const tmp = `${output}/__package_types_tmp__`; rimraf(tmp); mkdirp(tmp); const require = createRequire(import.meta.url); await emitDts({ - libRoot: config.package.source, + libRoot: input, svelteShimsPath: require.resolve('svelte2tsx/svelte-shims.d.ts'), declarationDir: path.relative(cwd, tmp) }); @@ -34,12 +36,6 @@ export async function emit_dts(config, cwd, files) { if (file.name.endsWith('.d.ts')) { handwritten.add(file.name); } - - if (!file.is_included) { - excluded.add(file.base + '.d.ts'); - excluded.add(file.base + '.d.mts'); - excluded.add(file.base + '.d.cts'); - } } // resolve $lib alias (TODO others), copy into package dir @@ -54,7 +50,7 @@ export async function emit_dts(config, cwd, files) { if (excluded.has(normalized)) continue; const source = fs.readFileSync(path.join(tmp, normalized), 'utf8'); - write(path.join(config.package.dir, normalized), resolve_lib_alias(normalized, source, config)); + write(path.join(output, normalized), resolve_aliases(input, normalized, source, alias)); } rimraf(tmp); diff --git a/packages/package/src/utils.js b/packages/package/src/utils.js index 0019706103a7..7526c10af0a1 100644 --- a/packages/package/src/utils.js +++ b/packages/package/src/utils.js @@ -3,21 +3,15 @@ import * as path from 'path'; import { posixify, mkdirp, walk } from './filesystem.js'; /** - * Resolves the `$lib` alias. + * Resolves aliases * - * TODO: make this more generic to also handle other aliases the user could have defined via - * `kit.alias`. Also investigate how to do this in a more robust way (right now regex string - * replacement is used). - * For more discussion see https://github.com/sveltejs/kit/pull/2453 - * - * @param {string} file Relative to the lib root + * @param {string} input + * @param {string} file Relative to the input * @param {string} content - * @param {import('./types').ValidatedConfig} config + * @param {Record} aliases * @returns {string} */ -export function resolve_lib_alias(file, content, config) { - const aliases = { $lib: path.resolve(config.package.source), ...(config.kit?.alias ?? {}) }; - +export function resolve_aliases(input, file, content, aliases) { /** * @param {string} match * @param {string} _ @@ -27,7 +21,7 @@ export function resolve_lib_alias(file, content, config) { for (const [alias, value] of Object.entries(aliases)) { if (!import_path.startsWith(alias)) continue; - const full_path = path.join(config.package.source, file); + const full_path = path.join(input, file); const full_import_path = path.join(value, import_path.slice(alias.length)); let resolved = posixify(path.relative(path.dirname(full_path), full_import_path)); resolved = resolved.startsWith('.') ? resolved : './' + resolved; @@ -67,22 +61,23 @@ export function write(file, contents) { } /** - * @param {import('./types').ValidatedConfig} config + * @param {string} input + * @param {string[]} extensions * @returns {import('./types').File[]} */ -export function scan(config) { - return walk(config.package.source).map((file) => analyze(config, file)); +export function scan(input, extensions) { + return walk(input).map((file) => analyze(file, extensions)); } /** - * @param {import('./types').ValidatedConfig} config * @param {string} file + * @param {string[]} extensions * @returns {import('./types').File} */ -export function analyze(config, file) { +export function analyze(file, extensions) { const name = posixify(file); - const svelte_extension = config.extensions.find((ext) => name.endsWith(ext)); + const svelte_extension = extensions.find((ext) => name.endsWith(ext)); const base = svelte_extension ? name : name.slice(0, -path.extname(name).length); @@ -98,54 +93,6 @@ export function analyze(config, file) { name, dest, base, - is_included: config.package.files(name), - is_exported: config.package.exports(name), is_svelte: !!svelte_extension }; } - -/** - * @param {string} cwd - * @param {import('./types').File[]} files - */ -export function generate_pkg(cwd, files) { - const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8')); - - // Remove fields that are specific to the original package.json - // See: https://pnpm.io/package_json#publishconfigdirectory - delete pkg.publishConfig?.directory; - delete pkg.linkDirectory?.directory; - delete pkg.scripts; - - pkg.type = 'module'; - - pkg.exports = { - './package.json': './package.json', - ...pkg.exports - }; - - /** @type {Record} */ - const clashes = {}; - - for (const file of files) { - if (file.is_included && file.is_exported) { - const original = `$lib/${file.name}`; - const entry = `./${file.dest}`; - const key = entry.replace(/\/index\.js$|(\/[^/]+)\.js$/, '$1'); - - if (clashes[key]) { - throw new Error( - `Duplicate "${key}" export. Please remove or rename either ${clashes[key]} or ${original}` - ); - } - - if (!pkg.exports[key]) { - pkg.exports[key] = entry; - } - - clashes[key] = original; - } - } - - return pkg; -} diff --git a/packages/package/src/validate.js b/packages/package/src/validate.js new file mode 100644 index 000000000000..83fe0c25ffbb --- /dev/null +++ b/packages/package/src/validate.js @@ -0,0 +1,173 @@ +import { existsSync, readFileSync } from 'node:fs'; +import colors from 'kleur'; + +/** + * @param {import("./types").Options} options + */ +export function create_validator(options) { + /** @type {Set} */ + const imports = new Set(); + let uses_import_meta = false; + let has_svelte_files = false; + + /** + * Checks a file content for problematic imports and things like `import.meta` + * @param {string} name + * @param {string} content + */ + function analyse_code(name, content) { + has_svelte_files = + has_svelte_files || + (options.config.extensions ?? ['.svelte']).some((ext) => name.endsWith(ext)); + uses_import_meta = uses_import_meta || content.includes('import.meta.env'); + + const file_imports = [ + ...content.matchAll(/from\s+('|")([^"';,]+?)\1/g), + ...content.matchAll(/import\s*\(\s*('|")([^"';,]+?)\1\s*\)/g) + ]; + for (const [, , import_path] of file_imports) { + if (import_path.startsWith('$app/')) { + imports.add(import_path); + } + } + } + + function validate() { + /** @type {Record} */ + const pkg = JSON.parse(readFileSync('package.json', 'utf-8')); + /** @type {string[]} */ + const warnings = []; + + if ( + imports.has('$app/environment') && + [...imports].filter((i) => i.startsWith('$app/')).length === 1 + ) { + warnings.push( + 'Avoid usage of `$app/environment` in your code, if you want to library to work for people not using SvelteKit (only regular Svelte, for example). ' + + 'Consider using packages like `esm-env` instead which provide cross-bundler-compatible environment variables.' + ); + } + + if (uses_import_meta) { + warnings.push( + 'Avoid usage of `import.meta.env` in your code. It requires a bundler to work. ' + + 'Consider using packages like `esm-env` instead which provide cross-bundler-compatible environment variables.' + ); + } + + if ( + !(pkg.dependencies?.['@sveltejs/kit'] || pkg.peerDependencies?.['@sveltejs/kit']) && + ([...imports].some((i) => i.startsWith('$app/')) || imports.has('@sveltejs/kit')) + ) { + warnings.push( + 'You are using SvelteKit-specific imports in your code, but you have not declared a dependency on `@sveltejs/kit` in your `package.json`. ' + + 'Add it to your `dependencies` or `peerDependencies`.' + ); + } + + if ( + !(pkg.dependencies?.svelte || pkg.peerDependencies?.svelte) && + (has_svelte_files || + [...imports].some((i) => i.startsWith('svelte/') || imports.has('svelte'))) + ) { + warnings.push( + 'You are using Svelte components or Svelte-specific imports in your code, but you have not declared a dependency on `svelte` in your `package.json`. ' + + 'Add it to your `dependencies` or `peerDependencies`.' + ); + } + + if (pkg.exports) { + const { conditions } = traverse_exports(pkg.exports); + if (has_svelte_files && !pkg.svelte && !conditions.has('svelte')) { + warnings.push( + 'You are using Svelte files, but did not declare a `svelte` condition in one of your `exports` in your `package.json`. ' + + 'Add a `svelte` condition to your `exports` to ensure that your package is recognized as Svelte package by tooling. ' + + 'See https://kit.svelte.dev/docs/packaging#anatomy-of-a-package-json-exports for more info' + ); + } + + if (pkg.svelte) { + const root_export = pkg.exports['.']; + if (!root_export) { + warnings.push( + 'You have a `svelte` field in your `package.json`, but no root export in your `exports`. Please align them so that bundlers will resolve consistently to the same file.' + ); + } else { + const { exports } = traverse_exports({ '.': root_export }); + if (![...exports].map(export_to_regex).some((_export) => _export.test(pkg.svelte))) { + warnings.push( + 'The `svelte` field in your `package.json` does not match any export in your root `exports`. Please align them so that bundlers will resolve consistently to the same file.' + ); + Object.keys(pkg.exports).map(export_to_regex); + } + } + } + } else { + warnings.push( + 'No `exports` field found in `package.json`, please provide one. ' + + 'See https://kit.svelte.dev/docs/packaging#anatomy-of-a-package-json-exports for more info' + ); + } + + // Just warnings, not errors, because + // - would be annoying in watch mode (would have to restart the server) + // - maybe there's a custom post-build script that fixes some of these + if (warnings.length) { + console.log( + colors + .bold() + .yellow('@sveltejs/package found the following issues while packaging your library:') + ); + for (const warning of warnings) { + console.log(colors.yellow(`${warning}\n`)); + } + } + } + + return { + analyse_code, + validate + }; +} + +/** + * @param {Record} exports_map + * @returns {{ exports: Set; conditions: Set }} + */ +function traverse_exports(exports_map) { + /** @type {Set} */ + const exports = new Set(); + /** @type {Set} */ + const conditions = new Set(); + + /** + * @param {Record} exports_map + * @param {boolean} is_first_level + */ + function traverse(exports_map, is_first_level) { + for (const key of Object.keys(exports_map ?? {})) { + if (is_first_level) { + conditions.add(key); + } + + const _export = exports_map[key]; + + if (typeof _export === 'string') { + exports.add(_export); + } else { + traverse(_export, false); + } + } + } + + traverse(exports_map, true); + + return { exports, conditions }; +} + +/** @param {string} _export */ +function export_to_regex(_export) { + // $& means the whole matched string + const regex_str = _export.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*'); + return new RegExp(`^${regex_str}$`); +} diff --git a/packages/package/test/errors/duplicate-export/jsconfig.json b/packages/package/test/errors/duplicate-export/jsconfig.json deleted file mode 100644 index f29fde21bafc..000000000000 --- a/packages/package/test/errors/duplicate-export/jsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "checkJs": true, - "baseUrl": ".", - "paths": { - "$lib/*": ["./src/lib/*"] - } - }, - "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] -} diff --git a/packages/package/test/errors/duplicate-export/package.json b/packages/package/test/errors/duplicate-export/package.json deleted file mode 100644 index 4befdcc6b97b..000000000000 --- a/packages/package/test/errors/duplicate-export/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "error-duplicate-export", - "private": true, - "version": "1.0.0", - "description": "validate duplicate export error output" -} diff --git a/packages/package/test/errors/duplicate-export/src/lib/utils.ts b/packages/package/test/errors/duplicate-export/src/lib/utils.ts deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/errors/duplicate-export/src/lib/utils/index.js b/packages/package/test/errors/duplicate-export/src/lib/utils/index.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/errors/duplicate-export/svelte.config.js b/packages/package/test/errors/duplicate-export/svelte.config.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/errors/duplicate-export/tsconfig.json b/packages/package/test/errors/duplicate-export/tsconfig.json deleted file mode 100644 index f29fde21bafc..000000000000 --- a/packages/package/test/errors/duplicate-export/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "checkJs": true, - "baseUrl": ".", - "paths": { - "$lib/*": ["./src/lib/*"] - } - }, - "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] -} diff --git a/packages/package/test/fixtures/assets/expected/package.json b/packages/package/test/fixtures/assets/expected/package.json deleted file mode 100644 index 8cd8408fe524..000000000000 --- a/packages/package/test/fixtures/assets/expected/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "assets", - "private": true, - "version": "1.0.0", - "description": "no tampering assets", - "type": "module", - "exports": { - "./package.json": "./package.json", - "./kit.png": "./kit.png" - } -} diff --git a/packages/package/test/fixtures/emitTypes-false/expected/package.json b/packages/package/test/fixtures/emitTypes-false/expected/package.json deleted file mode 100644 index c4ccc94b1311..000000000000 --- a/packages/package/test/fixtures/emitTypes-false/expected/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "emit-types", - "private": true, - "version": "1.0.0", - "description": "emitTypes settings disabled", - "type": "module", - "exports": { - "./package.json": "./package.json", - "./Test.svelte": "./Test.svelte", - "./Test2.svelte": "./Test2.svelte", - ".": "./index.js" - }, - "svelte": "./index.js" -} \ No newline at end of file diff --git a/packages/package/test/fixtures/emitTypes-false/svelte.config.js b/packages/package/test/fixtures/emitTypes-false/svelte.config.js index ff5361a7a094..f7e83e8493f9 100644 --- a/packages/package/test/fixtures/emitTypes-false/svelte.config.js +++ b/packages/package/test/fixtures/emitTypes-false/svelte.config.js @@ -1,7 +1,3 @@ -const config = { - package: { - emitTypes: false - } -}; +const config = {}; export default config; diff --git a/packages/package/test/fixtures/exports-merge/expected/Test.svelte b/packages/package/test/fixtures/exports-merge/expected/Test.svelte deleted file mode 100644 index e60789af6a74..000000000000 --- a/packages/package/test/fixtures/exports-merge/expected/Test.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/packages/package/test/fixtures/exports-merge/expected/Test.svelte.d.ts b/packages/package/test/fixtures/exports-merge/expected/Test.svelte.d.ts deleted file mode 100644 index 71bb3d88958e..000000000000 --- a/packages/package/test/fixtures/exports-merge/expected/Test.svelte.d.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** @typedef {typeof __propDef.props} TestProps */ -/** @typedef {typeof __propDef.events} TestEvents */ -/** @typedef {typeof __propDef.slots} TestSlots */ -export default class Test extends SvelteComponentTyped< - { - astring?: string; - }, - { - event: CustomEvent; - } & { - [evt: string]: CustomEvent; - }, - { - default: { - astring: string; - }; - } -> { - get astring(): string; -} -export type TestProps = typeof __propDef.props; -export type TestEvents = typeof __propDef.events; -export type TestSlots = typeof __propDef.slots; -import { SvelteComponentTyped } from 'svelte'; -declare const __propDef: { - props: { - astring?: string; - }; - events: { - event: CustomEvent; - } & { - [evt: string]: CustomEvent; - }; - slots: { - default: { - astring: string; - }; - }; -}; -export {}; diff --git a/packages/package/test/fixtures/exports-merge/expected/hello-world.d.ts b/packages/package/test/fixtures/exports-merge/expected/hello-world.d.ts deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/fixtures/exports-merge/expected/hello-world.js b/packages/package/test/fixtures/exports-merge/expected/hello-world.js deleted file mode 100644 index 08369f710356..000000000000 --- a/packages/package/test/fixtures/exports-merge/expected/hello-world.js +++ /dev/null @@ -1 +0,0 @@ -// hyphen in filename diff --git a/packages/package/test/fixtures/exports-merge/expected/icons123.d.ts b/packages/package/test/fixtures/exports-merge/expected/icons123.d.ts deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/fixtures/exports-merge/expected/icons123.js b/packages/package/test/fixtures/exports-merge/expected/icons123.js deleted file mode 100644 index 70161b512840..000000000000 --- a/packages/package/test/fixtures/exports-merge/expected/icons123.js +++ /dev/null @@ -1 +0,0 @@ -// numbers in filename diff --git a/packages/package/test/fixtures/exports-merge/expected/index.d.ts b/packages/package/test/fixtures/exports-merge/expected/index.d.ts deleted file mode 100644 index 4c44188c3648..000000000000 --- a/packages/package/test/fixtures/exports-merge/expected/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as Test } from './Test.svelte'; diff --git a/packages/package/test/fixtures/exports-merge/expected/index.js b/packages/package/test/fixtures/exports-merge/expected/index.js deleted file mode 100644 index 4c44188c3648..000000000000 --- a/packages/package/test/fixtures/exports-merge/expected/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as Test } from './Test.svelte'; diff --git a/packages/package/test/fixtures/exports-merge/expected/internal/Test.svelte b/packages/package/test/fixtures/exports-merge/expected/internal/Test.svelte deleted file mode 100644 index 852fe8cd5dbc..000000000000 --- a/packages/package/test/fixtures/exports-merge/expected/internal/Test.svelte +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/packages/package/test/fixtures/exports-merge/expected/internal/Test.svelte.d.ts b/packages/package/test/fixtures/exports-merge/expected/internal/Test.svelte.d.ts deleted file mode 100644 index c6ff47603e75..000000000000 --- a/packages/package/test/fixtures/exports-merge/expected/internal/Test.svelte.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** @typedef {typeof __propDef.props} TestProps */ -/** @typedef {typeof __propDef.events} TestEvents */ -/** @typedef {typeof __propDef.slots} TestSlots */ -export default class Test extends SvelteComponentTyped< - { - foo: boolean; - }, - { - [evt: string]: CustomEvent; - }, - {} -> {} -export type TestProps = typeof __propDef.props; -export type TestEvents = typeof __propDef.events; -export type TestSlots = typeof __propDef.slots; -import { SvelteComponentTyped } from 'svelte'; -declare const __propDef: { - props: { - foo: import('./foo').Foo; - }; - events: { - [evt: string]: CustomEvent; - }; - slots: {}; -}; -export {}; diff --git a/packages/package/test/fixtures/exports-merge/expected/internal/_Private.svelte b/packages/package/test/fixtures/exports-merge/expected/internal/_Private.svelte deleted file mode 100644 index 605c3c2de038..000000000000 --- a/packages/package/test/fixtures/exports-merge/expected/internal/_Private.svelte +++ /dev/null @@ -1 +0,0 @@ -This is a private component and should be excluded diff --git a/packages/package/test/fixtures/exports-merge/expected/internal/_Private.svelte.d.ts b/packages/package/test/fixtures/exports-merge/expected/internal/_Private.svelte.d.ts deleted file mode 100644 index 55647e2f8b99..000000000000 --- a/packages/package/test/fixtures/exports-merge/expected/internal/_Private.svelte.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** @typedef {typeof __propDef.props} PrivateProps */ -/** @typedef {typeof __propDef.events} PrivateEvents */ -/** @typedef {typeof __propDef.slots} PrivateSlots */ -export default class Private extends SvelteComponentTyped< -{ - [x:string]: never; -}, { - [evt: string]: CustomEvent; -}, -{} -> { -} -export type PrivateProps = typeof __propDef.props; -export type PrivateEvents = typeof __propDef.events; -export type PrivateSlots = typeof __propDef.slots; -import { SvelteComponentTyped } from "svelte"; -declare const __propDef: { - props: { - [x:string]: never; - }; - events: { - [evt: string]: CustomEvent; - }; - slots: {}; -}; -export {}; diff --git a/packages/package/test/fixtures/exports-merge/expected/internal/constants.d.ts b/packages/package/test/fixtures/exports-merge/expected/internal/constants.d.ts deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/fixtures/exports-merge/expected/internal/constants.js b/packages/package/test/fixtures/exports-merge/expected/internal/constants.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/fixtures/exports-merge/expected/internal/foo.d.ts b/packages/package/test/fixtures/exports-merge/expected/internal/foo.d.ts deleted file mode 100644 index c941ad7b1ce6..000000000000 --- a/packages/package/test/fixtures/exports-merge/expected/internal/foo.d.ts +++ /dev/null @@ -1 +0,0 @@ -export type Foo = boolean; diff --git a/packages/package/test/fixtures/exports-merge/expected/package.json b/packages/package/test/fixtures/exports-merge/expected/package.json deleted file mode 100644 index 085f6ac9743d..000000000000 --- a/packages/package/test/fixtures/exports-merge/expected/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "exports-replace", - "private": true, - "version": "1.0.0", - "description": "merges exports field", - "exports": { - "./internal/constants": "./internal/constants.js", - "./internal/Test.svelte": "./internal/Test.svelte", - "./hello-world": "./hello-world.js", - "./icons123": "./icons123.js", - "./Test.svelte": "./Test.svelte", - "./why_underscore": "./why_underscore.js", - ".": { - "import": "./index.js" - }, - "./constants": "./internal/constants.js", - "./Test": "./Test.svelte", - "./package.json": "./package.json" - }, - "type": "module", - "svelte": "./index.js" -} \ No newline at end of file diff --git a/packages/package/test/fixtures/exports-merge/expected/why_underscore.d.ts b/packages/package/test/fixtures/exports-merge/expected/why_underscore.d.ts deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/fixtures/exports-merge/expected/why_underscore.js b/packages/package/test/fixtures/exports-merge/expected/why_underscore.js deleted file mode 100644 index fb22c7cffcd5..000000000000 --- a/packages/package/test/fixtures/exports-merge/expected/why_underscore.js +++ /dev/null @@ -1 +0,0 @@ -// underscore in filename diff --git a/packages/package/test/fixtures/exports-merge/jsconfig.json b/packages/package/test/fixtures/exports-merge/jsconfig.json deleted file mode 100644 index 0967ef424bce..000000000000 --- a/packages/package/test/fixtures/exports-merge/jsconfig.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/package/test/fixtures/exports-merge/package.json b/packages/package/test/fixtures/exports-merge/package.json deleted file mode 100644 index 0033602dd2c7..000000000000 --- a/packages/package/test/fixtures/exports-merge/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "exports-replace", - "private": true, - "version": "1.0.0", - "description": "merges exports field", - "exports": { - ".": { - "import": "./index.js" - }, - "./constants": "./internal/constants.js", - "./Test": "./Test.svelte" - } -} diff --git a/packages/package/test/fixtures/exports-merge/src/lib/Test.svelte b/packages/package/test/fixtures/exports-merge/src/lib/Test.svelte deleted file mode 100644 index e60789af6a74..000000000000 --- a/packages/package/test/fixtures/exports-merge/src/lib/Test.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/packages/package/test/fixtures/exports-merge/src/lib/hello-world.js b/packages/package/test/fixtures/exports-merge/src/lib/hello-world.js deleted file mode 100644 index 08369f710356..000000000000 --- a/packages/package/test/fixtures/exports-merge/src/lib/hello-world.js +++ /dev/null @@ -1 +0,0 @@ -// hyphen in filename diff --git a/packages/package/test/fixtures/exports-merge/src/lib/icons123.js b/packages/package/test/fixtures/exports-merge/src/lib/icons123.js deleted file mode 100644 index 70161b512840..000000000000 --- a/packages/package/test/fixtures/exports-merge/src/lib/icons123.js +++ /dev/null @@ -1 +0,0 @@ -// numbers in filename diff --git a/packages/package/test/fixtures/exports-merge/src/lib/index.js b/packages/package/test/fixtures/exports-merge/src/lib/index.js deleted file mode 100644 index 4c44188c3648..000000000000 --- a/packages/package/test/fixtures/exports-merge/src/lib/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as Test } from './Test.svelte'; diff --git a/packages/package/test/fixtures/exports-merge/src/lib/internal/Test.svelte b/packages/package/test/fixtures/exports-merge/src/lib/internal/Test.svelte deleted file mode 100644 index 852fe8cd5dbc..000000000000 --- a/packages/package/test/fixtures/exports-merge/src/lib/internal/Test.svelte +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/packages/package/test/fixtures/exports-merge/src/lib/internal/_Private.svelte b/packages/package/test/fixtures/exports-merge/src/lib/internal/_Private.svelte deleted file mode 100644 index 605c3c2de038..000000000000 --- a/packages/package/test/fixtures/exports-merge/src/lib/internal/_Private.svelte +++ /dev/null @@ -1 +0,0 @@ -This is a private component and should be excluded diff --git a/packages/package/test/fixtures/exports-merge/src/lib/internal/constants.js b/packages/package/test/fixtures/exports-merge/src/lib/internal/constants.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/fixtures/exports-merge/src/lib/internal/foo.d.ts b/packages/package/test/fixtures/exports-merge/src/lib/internal/foo.d.ts deleted file mode 100644 index c941ad7b1ce6..000000000000 --- a/packages/package/test/fixtures/exports-merge/src/lib/internal/foo.d.ts +++ /dev/null @@ -1 +0,0 @@ -export type Foo = boolean; diff --git a/packages/package/test/fixtures/exports-merge/src/lib/why_underscore.js b/packages/package/test/fixtures/exports-merge/src/lib/why_underscore.js deleted file mode 100644 index fb22c7cffcd5..000000000000 --- a/packages/package/test/fixtures/exports-merge/src/lib/why_underscore.js +++ /dev/null @@ -1 +0,0 @@ -// underscore in filename diff --git a/packages/package/test/fixtures/exports-merge/svelte.config.js b/packages/package/test/fixtures/exports-merge/svelte.config.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/fixtures/exports-replace/expected/Test.svelte b/packages/package/test/fixtures/exports-replace/expected/Test.svelte deleted file mode 100644 index e60789af6a74..000000000000 --- a/packages/package/test/fixtures/exports-replace/expected/Test.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/packages/package/test/fixtures/exports-replace/expected/Test.svelte.d.ts b/packages/package/test/fixtures/exports-replace/expected/Test.svelte.d.ts deleted file mode 100644 index 71bb3d88958e..000000000000 --- a/packages/package/test/fixtures/exports-replace/expected/Test.svelte.d.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** @typedef {typeof __propDef.props} TestProps */ -/** @typedef {typeof __propDef.events} TestEvents */ -/** @typedef {typeof __propDef.slots} TestSlots */ -export default class Test extends SvelteComponentTyped< - { - astring?: string; - }, - { - event: CustomEvent; - } & { - [evt: string]: CustomEvent; - }, - { - default: { - astring: string; - }; - } -> { - get astring(): string; -} -export type TestProps = typeof __propDef.props; -export type TestEvents = typeof __propDef.events; -export type TestSlots = typeof __propDef.slots; -import { SvelteComponentTyped } from 'svelte'; -declare const __propDef: { - props: { - astring?: string; - }; - events: { - event: CustomEvent; - } & { - [evt: string]: CustomEvent; - }; - slots: { - default: { - astring: string; - }; - }; -}; -export {}; diff --git a/packages/package/test/fixtures/exports-replace/expected/index.d.ts b/packages/package/test/fixtures/exports-replace/expected/index.d.ts deleted file mode 100644 index 4c44188c3648..000000000000 --- a/packages/package/test/fixtures/exports-replace/expected/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as Test } from './Test.svelte'; diff --git a/packages/package/test/fixtures/exports-replace/expected/index.js b/packages/package/test/fixtures/exports-replace/expected/index.js deleted file mode 100644 index 4c44188c3648..000000000000 --- a/packages/package/test/fixtures/exports-replace/expected/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as Test } from './Test.svelte'; diff --git a/packages/package/test/fixtures/exports-replace/expected/internal/Test.svelte b/packages/package/test/fixtures/exports-replace/expected/internal/Test.svelte deleted file mode 100644 index 852fe8cd5dbc..000000000000 --- a/packages/package/test/fixtures/exports-replace/expected/internal/Test.svelte +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/packages/package/test/fixtures/exports-replace/expected/internal/Test.svelte.d.ts b/packages/package/test/fixtures/exports-replace/expected/internal/Test.svelte.d.ts deleted file mode 100644 index c6ff47603e75..000000000000 --- a/packages/package/test/fixtures/exports-replace/expected/internal/Test.svelte.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** @typedef {typeof __propDef.props} TestProps */ -/** @typedef {typeof __propDef.events} TestEvents */ -/** @typedef {typeof __propDef.slots} TestSlots */ -export default class Test extends SvelteComponentTyped< - { - foo: boolean; - }, - { - [evt: string]: CustomEvent; - }, - {} -> {} -export type TestProps = typeof __propDef.props; -export type TestEvents = typeof __propDef.events; -export type TestSlots = typeof __propDef.slots; -import { SvelteComponentTyped } from 'svelte'; -declare const __propDef: { - props: { - foo: import('./foo').Foo; - }; - events: { - [evt: string]: CustomEvent; - }; - slots: {}; -}; -export {}; diff --git a/packages/package/test/fixtures/exports-replace/expected/internal/foo.d.ts b/packages/package/test/fixtures/exports-replace/expected/internal/foo.d.ts deleted file mode 100644 index c941ad7b1ce6..000000000000 --- a/packages/package/test/fixtures/exports-replace/expected/internal/foo.d.ts +++ /dev/null @@ -1 +0,0 @@ -export type Foo = boolean; diff --git a/packages/package/test/fixtures/exports-replace/expected/package.json b/packages/package/test/fixtures/exports-replace/expected/package.json deleted file mode 100644 index 45ec663ffb09..000000000000 --- a/packages/package/test/fixtures/exports-replace/expected/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "exports-replace", - "private": true, - "version": "1.0.0", - "description": "replaces exports field", - "type": "module", - "exports": { - ".": { - "import": "./index.js" - }, - "./package.json": "./package.json" - }, - "svelte": "./Test.svelte" -} \ No newline at end of file diff --git a/packages/package/test/fixtures/exports-replace/jsconfig.json b/packages/package/test/fixtures/exports-replace/jsconfig.json deleted file mode 100644 index 0967ef424bce..000000000000 --- a/packages/package/test/fixtures/exports-replace/jsconfig.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/package/test/fixtures/exports-replace/package.json b/packages/package/test/fixtures/exports-replace/package.json deleted file mode 100644 index 96c1987386d1..000000000000 --- a/packages/package/test/fixtures/exports-replace/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "exports-replace", - "private": true, - "version": "1.0.0", - "description": "replaces exports field", - "type": "module", - "exports": { - ".": { - "import": "./index.js" - } - }, - "svelte": "./Test.svelte" -} diff --git a/packages/package/test/fixtures/exports-replace/src/lib/Test.svelte b/packages/package/test/fixtures/exports-replace/src/lib/Test.svelte deleted file mode 100644 index e60789af6a74..000000000000 --- a/packages/package/test/fixtures/exports-replace/src/lib/Test.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/packages/package/test/fixtures/exports-replace/src/lib/index.js b/packages/package/test/fixtures/exports-replace/src/lib/index.js deleted file mode 100644 index 4c44188c3648..000000000000 --- a/packages/package/test/fixtures/exports-replace/src/lib/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as Test } from './Test.svelte'; diff --git a/packages/package/test/fixtures/exports-replace/src/lib/internal/Test.svelte b/packages/package/test/fixtures/exports-replace/src/lib/internal/Test.svelte deleted file mode 100644 index 852fe8cd5dbc..000000000000 --- a/packages/package/test/fixtures/exports-replace/src/lib/internal/Test.svelte +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/packages/package/test/fixtures/exports-replace/src/lib/internal/foo.d.ts b/packages/package/test/fixtures/exports-replace/src/lib/internal/foo.d.ts deleted file mode 100644 index c941ad7b1ce6..000000000000 --- a/packages/package/test/fixtures/exports-replace/src/lib/internal/foo.d.ts +++ /dev/null @@ -1 +0,0 @@ -export type Foo = boolean; diff --git a/packages/package/test/fixtures/exports-replace/svelte.config.js b/packages/package/test/fixtures/exports-replace/svelte.config.js deleted file mode 100644 index 8436b7feae10..000000000000 --- a/packages/package/test/fixtures/exports-replace/svelte.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const config = { - package: { - exports: () => false - } -}; - -export default config; diff --git a/packages/package/test/fixtures/files-exclude/expected/Test.svelte b/packages/package/test/fixtures/files-exclude/expected/Test.svelte deleted file mode 100644 index e60789af6a74..000000000000 --- a/packages/package/test/fixtures/files-exclude/expected/Test.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/packages/package/test/fixtures/files-exclude/expected/Test.svelte.d.ts b/packages/package/test/fixtures/files-exclude/expected/Test.svelte.d.ts deleted file mode 100644 index 71bb3d88958e..000000000000 --- a/packages/package/test/fixtures/files-exclude/expected/Test.svelte.d.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** @typedef {typeof __propDef.props} TestProps */ -/** @typedef {typeof __propDef.events} TestEvents */ -/** @typedef {typeof __propDef.slots} TestSlots */ -export default class Test extends SvelteComponentTyped< - { - astring?: string; - }, - { - event: CustomEvent; - } & { - [evt: string]: CustomEvent; - }, - { - default: { - astring: string; - }; - } -> { - get astring(): string; -} -export type TestProps = typeof __propDef.props; -export type TestEvents = typeof __propDef.events; -export type TestSlots = typeof __propDef.slots; -import { SvelteComponentTyped } from 'svelte'; -declare const __propDef: { - props: { - astring?: string; - }; - events: { - event: CustomEvent; - } & { - [evt: string]: CustomEvent; - }; - slots: { - default: { - astring: string; - }; - }; -}; -export {}; diff --git a/packages/package/test/fixtures/files-exclude/expected/index.d.ts b/packages/package/test/fixtures/files-exclude/expected/index.d.ts deleted file mode 100644 index f0eb1f79b695..000000000000 --- a/packages/package/test/fixtures/files-exclude/expected/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export const hello: 'world'; diff --git a/packages/package/test/fixtures/files-exclude/expected/index.js b/packages/package/test/fixtures/files-exclude/expected/index.js deleted file mode 100644 index 35f468bf4856..000000000000 --- a/packages/package/test/fixtures/files-exclude/expected/index.js +++ /dev/null @@ -1 +0,0 @@ -export const hello = 'world'; diff --git a/packages/package/test/fixtures/files-exclude/expected/internal/index.d.ts b/packages/package/test/fixtures/files-exclude/expected/internal/index.d.ts deleted file mode 100644 index e60ad9862f02..000000000000 --- a/packages/package/test/fixtures/files-exclude/expected/internal/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export const foo: 'bar'; diff --git a/packages/package/test/fixtures/files-exclude/expected/internal/index.js b/packages/package/test/fixtures/files-exclude/expected/internal/index.js deleted file mode 100644 index c155820bf773..000000000000 --- a/packages/package/test/fixtures/files-exclude/expected/internal/index.js +++ /dev/null @@ -1 +0,0 @@ -export const foo = 'bar'; diff --git a/packages/package/test/fixtures/files-exclude/expected/package.json b/packages/package/test/fixtures/files-exclude/expected/package.json deleted file mode 100644 index 2303af531750..000000000000 --- a/packages/package/test/fixtures/files-exclude/expected/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "files-exclude", - "private": true, - "version": "1.0.0", - "description": "files.exclude settings configured", - "type": "module", - "exports": { - "./package.json": "./package.json", - "./internal": "./internal/index.js", - "./Test.svelte": "./Test.svelte", - ".": "./index.js" - }, - "svelte": "./index.js" -} \ No newline at end of file diff --git a/packages/package/test/fixtures/files-exclude/jsconfig.json b/packages/package/test/fixtures/files-exclude/jsconfig.json deleted file mode 100644 index 0967ef424bce..000000000000 --- a/packages/package/test/fixtures/files-exclude/jsconfig.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/package/test/fixtures/files-exclude/package.json b/packages/package/test/fixtures/files-exclude/package.json deleted file mode 100644 index c81333c30b11..000000000000 --- a/packages/package/test/fixtures/files-exclude/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "files-exclude", - "private": true, - "version": "1.0.0", - "description": "files.exclude settings configured", - "type": "module" -} diff --git a/packages/package/test/fixtures/files-exclude/src/lib/Test.exclude.svelte b/packages/package/test/fixtures/files-exclude/src/lib/Test.exclude.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/fixtures/files-exclude/src/lib/Test.svelte b/packages/package/test/fixtures/files-exclude/src/lib/Test.svelte deleted file mode 100644 index e60789af6a74..000000000000 --- a/packages/package/test/fixtures/files-exclude/src/lib/Test.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/packages/package/test/fixtures/files-exclude/src/lib/check.mjs b/packages/package/test/fixtures/files-exclude/src/lib/check.mjs deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/fixtures/files-exclude/src/lib/empty/Test.exclude.svelte b/packages/package/test/fixtures/files-exclude/src/lib/empty/Test.exclude.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/fixtures/files-exclude/src/lib/exclude.js b/packages/package/test/fixtures/files-exclude/src/lib/exclude.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/fixtures/files-exclude/src/lib/index.js b/packages/package/test/fixtures/files-exclude/src/lib/index.js deleted file mode 100644 index 35f468bf4856..000000000000 --- a/packages/package/test/fixtures/files-exclude/src/lib/index.js +++ /dev/null @@ -1 +0,0 @@ -export const hello = 'world'; diff --git a/packages/package/test/fixtures/files-exclude/src/lib/internal/build.mjs b/packages/package/test/fixtures/files-exclude/src/lib/internal/build.mjs deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/fixtures/files-exclude/src/lib/internal/exclude.js b/packages/package/test/fixtures/files-exclude/src/lib/internal/exclude.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/package/test/fixtures/files-exclude/src/lib/internal/index.js b/packages/package/test/fixtures/files-exclude/src/lib/internal/index.js deleted file mode 100644 index c155820bf773..000000000000 --- a/packages/package/test/fixtures/files-exclude/src/lib/internal/index.js +++ /dev/null @@ -1 +0,0 @@ -export const foo = 'bar'; diff --git a/packages/package/test/fixtures/files-exclude/svelte.config.js b/packages/package/test/fixtures/files-exclude/svelte.config.js deleted file mode 100644 index 3f46f0e40ada..000000000000 --- a/packages/package/test/fixtures/files-exclude/svelte.config.js +++ /dev/null @@ -1,11 +0,0 @@ -const config = { - package: { - files(filepath) { - const ext = filepath.slice(filepath.lastIndexOf('.') + 1); - if (ext === 'js' || ext === 'svelte') return !filepath.includes('exclude'); - return ext !== 'mjs'; - } - } -}; - -export default config; diff --git a/packages/package/test/fixtures/javascript/expected/ReadMe.md b/packages/package/test/fixtures/javascript/expected/ReadMe.md deleted file mode 100644 index e965047ad7c5..000000000000 --- a/packages/package/test/fixtures/javascript/expected/ReadMe.md +++ /dev/null @@ -1 +0,0 @@ -Hello diff --git a/packages/package/test/fixtures/javascript/expected/package.json b/packages/package/test/fixtures/javascript/expected/package.json deleted file mode 100644 index bbabe850c2b2..000000000000 --- a/packages/package/test/fixtures/javascript/expected/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "javascript", - "private": true, - "version": "1.0.0", - "description": "standard javascript package", - "type": "module", - "exports": { - "./package.json": "./package.json", - "./internal": "./internal/index.js", - "./internal/Test.svelte": "./internal/Test.svelte", - "./Test.svelte": "./Test.svelte", - "./Test2.svelte": "./Test2.svelte", - "./utils": "./utils.js", - ".": "./index.js" - }, - "svelte": "./index.js" -} \ No newline at end of file diff --git a/packages/package/test/fixtures/resolve-alias/expected/package.json b/packages/package/test/fixtures/resolve-alias/expected/package.json deleted file mode 100644 index b0cea47c9a8c..000000000000 --- a/packages/package/test/fixtures/resolve-alias/expected/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "resolve-alias", - "private": true, - "version": "1.0.0", - "description": "package using $lib alias", - "type": "module", - "exports": { - "./package.json": "./package.json", - "./Test.svelte": "./Test.svelte", - "./utils": "./utils/index.js", - ".": "./index.js", - "./baz": "./baz.js", - "./sub/bar": "./sub/bar.js", - "./sub/foo": "./sub/foo.js" - }, - "svelte": "./index.js" -} diff --git a/packages/package/test/fixtures/svelte-kit/expected/package.json b/packages/package/test/fixtures/svelte-kit/expected/package.json deleted file mode 100644 index 9a4640878bf1..000000000000 --- a/packages/package/test/fixtures/svelte-kit/expected/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "svelte-kit", - "private": true, - "version": "1.0.0", - "type": "module", - "description": "SvelteKit library project", - "exports": { - "./package.json": "./package.json", - "./Test.svelte": "./Test.svelte", - ".": "./index.js" - }, - "svelte": "./index.js" -} \ No newline at end of file diff --git a/packages/package/test/fixtures/typescript/expected/package.json b/packages/package/test/fixtures/typescript/expected/package.json deleted file mode 100644 index 91b6a3b5b264..000000000000 --- a/packages/package/test/fixtures/typescript/expected/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "typescript", - "private": true, - "version": "1.0.0", - "description": "standard typescript package", - "type": "module", - "exports": { - "./package.json": "./package.json", - "./Plain.svelte": "./Plain.svelte", - "./Test.svelte": "./Test.svelte", - "./Test2.svelte": "./Test2.svelte", - "./utils": "./utils.js", - ".": "./index.js" - }, - "svelte": "./index.js" -} \ No newline at end of file diff --git a/packages/package/test/index.js b/packages/package/test/index.js index 6a06e24bb1f5..a0ab8b4b379c 100644 --- a/packages/package/test/index.js +++ b/packages/package/test/index.js @@ -15,42 +15,48 @@ const __dirname = join(__filename, '..'); /** * @param {string} path + * @param {Partial} [options] */ -async function test_make_package(path) { +async function test_make_package(path, options) { const cwd = join(__dirname, 'fixtures', path); const ewd = join(cwd, 'expected'); - const pwd = join(cwd, 'package'); + const output = join(cwd, 'dist'); - try { - const config = await load_config({ cwd }); - config.package.dir = resolve(cwd, config.package.dir); - - await build(config, cwd); - const expected_files = walk(ewd, true); - const actual_files = walk(pwd, true); - - assert.equal(actual_files, expected_files); - - const extensions = ['.json', '.svelte', '.ts', 'js']; - for (const file of actual_files) { - const pathname = join(pwd, file); - if (fs.statSync(pathname).isDirectory()) continue; - assert.ok(expected_files.includes(file), `Did not expect ${file} in ${path}`); - - const expected = fs.readFileSync(join(ewd, file)); - const actual = fs.readFileSync(join(pwd, file)); - const err_msg = `Expected equal file contents for ${file} in ${path}`; - - if (extensions.some((ext) => pathname.endsWith(ext))) { - const expected_content = format(file, expected.toString('utf-8')); - const actual_content = format(file, actual.toString('utf-8')); - assert.fixture(actual_content, expected_content, err_msg); - } else { - assert.ok(expected.equals(actual), err_msg); - } + const config = await load_config({ cwd }); + + const input = resolve(cwd, config.kit?.files?.lib ?? 'src/lib'); + + await build({ + cwd, + input, + output, + types: true, + config, + ...options + }); + + const expected_files = walk(ewd, true); + const actual_files = walk(output, true); + + assert.equal(actual_files, expected_files); + + const extensions = ['.json', '.svelte', '.ts', 'js']; + for (const file of actual_files) { + const pathname = join(output, file); + if (fs.statSync(pathname).isDirectory()) continue; + assert.ok(expected_files.includes(file), `Did not expect ${file} in ${path}`); + + const expected = fs.readFileSync(join(ewd, file)); + const actual = fs.readFileSync(join(output, file)); + const err_msg = `Expected equal file contents for ${file} in ${path}`; + + if (extensions.some((ext) => pathname.endsWith(ext))) { + const expected_content = format(file, expected.toString('utf-8')); + const actual_content = format(file, actual.toString('utf-8')); + assert.fixture(actual_content, expected_content, err_msg); + } else { + assert.ok(expected.equals(actual), err_msg); } - } finally { - rimraf(pwd); } } @@ -75,26 +81,24 @@ function format(file, content) { for (const dir of fs.readdirSync(join(__dirname, 'errors'))) { test(`package error [${dir}]`, async () => { const cwd = join(__dirname, 'errors', dir); - const pwd = join(cwd, 'package'); + const output = join(cwd, 'dist'); const config = await load_config({ cwd }); - config.package.dir = resolve(cwd, config.package.dir); + + const input = resolve(cwd, config.kit?.files?.lib ?? 'src/lib'); try { - await build(config, cwd); + await build({ cwd, input, output, types: true, config }); assert.unreachable('Must not pass build'); } catch (/** @type {any} */ error) { assert.instance(error, Error); switch (dir) { - case 'duplicate-export': + case 'no-lib-folder': assert.match( - error.message, - 'Duplicate "./utils" export. Please remove or rename either $lib/utils/index.js or $lib/utils.ts' + error.message.replace(/\\/g, '/'), + `test/errors/no-lib-folder/src/lib does not exist` ); break; - case 'no-lib-folder': - assert.match(error.message, `${join(cwd, 'src', 'lib')} does not exist`); - break; // TODO: non-existent tsconfig passes without error // it detects tsconfig in packages/kit instead and creates package folder // in packages/kit/package, not sure how to handle and test this yet @@ -107,7 +111,7 @@ for (const dir of fs.readdirSync(join(__dirname, 'errors'))) { break; } } finally { - rimraf(pwd); + rimraf(output); } }); } @@ -127,19 +131,7 @@ test('create package and assets are not tampered', async () => { }); test('create package with emitTypes settings disabled', async () => { - await test_make_package('emitTypes-false'); -}); - -test('create package and properly merge exports map', async () => { - await test_make_package('exports-merge'); -}); - -test('create package and properly exclude all exports', async () => { - await test_make_package('exports-replace'); -}); - -test('create package with files.exclude settings', async () => { - await test_make_package('files-exclude'); + await test_make_package('emitTypes-false', { types: false }); }); test('create package and resolves $lib alias', async () => { @@ -156,9 +148,14 @@ if (!process.env.CI) { const cwd = join(__dirname, 'watch'); const config = await load_config({ cwd }); - config.package.dir = resolve(cwd, config.package.dir); - const { watcher, ready, settled } = await watch(config, cwd); + const { watcher, ready, settled } = await watch({ + cwd, + input: 'src/lib', + output: 'package', + types: true, + config + }); /** @param {string} file */ function compare(file) { diff --git a/packages/package/types/index.d.ts b/packages/package/types/index.d.ts deleted file mode 100644 index 0e1ba39b6364..000000000000 --- a/packages/package/types/index.d.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { CompileOptions } from 'svelte/types/compiler/interfaces'; // TODO bump Svelte version, then export from right path - -export interface PackageConfig { - /** - * Path to the files that should be packaged. - * If this is used inside SvelteKit, this is read from `kit.files.lib` if unset. Defaults to `src/lib` otherwise. - */ - source?: string; - /** - * Output directory - */ - dir?: string; - /** - * Whether to emit type definition files. Defaults to `true`. - */ - emitTypes?: boolean; - /** - * Function that determines if the given filepath will be included in the `exports` field - * of the `package.json`. - */ - exports?(filepath: string): boolean; - /** - * Function that determines if the given file is part of the output. - */ - files?(filepath: string): boolean; -} - -export interface Config { - compilerOptions?: CompileOptions; - extensions?: string[]; - kit?: any; - package?: PackageConfig; - preprocess?: any; -}