diff --git a/packages/sv-utils/package.json b/packages/sv-utils/package.json index 83a31a026..4eafe637f 100644 --- a/packages/sv-utils/package.json +++ b/packages/sv-utils/package.json @@ -38,7 +38,6 @@ }, "keywords": [ "sv", - "sv-add", "svelte", "sveltekit" ] diff --git a/packages/sv/src/cli/create.ts b/packages/sv/src/cli/create.ts index c10e83500..b31804536 100644 --- a/packages/sv/src/cli/create.ts +++ b/packages/sv/src/cli/create.ts @@ -263,6 +263,14 @@ async function createProject(cwd: ProjectPath, options: Options) { const parentDirName = path.basename(path.dirname(projectPath)); const projectName = parentDirName.startsWith('@') ? `${parentDirName}/${basename}` : basename; + if (template === 'addon' && !projectName.startsWith('@')) { + // At this stage, we don't support un-scoped add-ons + // FYI: a demo exists for `npx sv add my-cool-addon` + common.errorAndExit( + `Community add-ons must be published under an npm org (e.g. ${color.command('@my-org/sv')}). Unscoped package names are not supported at this stage.` + ); + } + if (template === 'addon' && options.add.length > 0) { common.errorAndExit( `The ${color.command('--add')} flag cannot be used with the ${color.command('addon')} template.` diff --git a/packages/sv/src/cli/tests/cli.ts b/packages/sv/src/cli/tests/cli.ts index fe56c126f..fe8e37883 100644 --- a/packages/sv/src/cli/tests/cli.ts +++ b/packages/sv/src/cli/tests/cli.ts @@ -123,8 +123,9 @@ describe('cli', () => { // replace sv and sv-utils versions in package.json for tests const packageJsonPath = path.resolve(testOutputPath, 'package.json'); const { data: packageJson } = parse.json(fs.readFileSync(packageJsonPath, 'utf-8')); - packageJson.dependencies['sv'] = 'file:../../../..'; - packageJson.dependencies['@sveltejs/sv-utils'] = 'file:../../../../sv-utils'; + packageJson.peerDependencies['sv'] = 'file:../../../..'; + packageJson.devDependencies['sv'] = 'file:../../../..'; + packageJson.devDependencies['@sveltejs/sv-utils'] = 'file:../../../../sv-utils'; fs.writeFileSync( packageJsonPath, JSON.stringify(packageJson, null, 3).replaceAll(' ', '\t') diff --git a/packages/sv/src/cli/tests/snapshots/@my-org/sv/CONTRIBUTING.md b/packages/sv/src/cli/tests/snapshots/@my-org/sv/CONTRIBUTING.md index 20e629e3c..d5da26c73 100644 --- a/packages/sv/src/cli/tests/snapshots/@my-org/sv/CONTRIBUTING.md +++ b/packages/sv/src/cli/tests/snapshots/@my-org/sv/CONTRIBUTING.md @@ -24,15 +24,25 @@ Your `add-on` should: - export a function that returns a `defineAddon` object. - have a `package.json` with an `exports` field that points to the main entry point of the add-on. -## Sharing your add-on +## Building -When you're ready to publish your add-on to npm, run: +Your add-on is bundled with [tsdown](https://tsdown.dev/) into a single file in `dist/`. This bundles everything except `sv` (which is a peer dependency provided at runtime). -```shell +```sh +npm run build +``` + +## Publishing + +When you're ready to publish your add-on to npm: + +```sh npm login npm publish ``` +> `prepublishOnly` will automatically run the build before publishing. + ## Things to be aware of -Community add-ons are **not permitted** to have any external dependencies outside of `sv`. If the use of a dependency is absolutely necessary, then they can be bundled using a bundler of your choosing (e.g. Rollup, Rolldown, tsup, etc.). +Community add-ons must have `sv` as a `peerDependency` and should **not** have any `dependencies`. Everything else (including `@sveltejs/sv-utils`) is bundled at build time by tsdown. diff --git a/packages/sv/src/cli/tests/snapshots/@my-org/sv/package.json b/packages/sv/src/cli/tests/snapshots/@my-org/sv/package.json index b124bfdca..03ae9cd99 100644 --- a/packages/sv/src/cli/tests/snapshots/@my-org/sv/package.json +++ b/packages/sv/src/cli/tests/snapshots/@my-org/sv/package.json @@ -8,30 +8,37 @@ "demo-create": "sv create demo --types ts --template minimal --no-add-ons --no-install", "demo-add": "sv add file:../ --cwd demo --no-git-check --no-install", "demo-add:ci": "sv add file:../=who:you --cwd demo --no-git-check --no-download-check --no-install", + "build": "tsdown", + "prepublishOnly": "npm run build", "test": "vitest run" }, "files": [ - "src", - "!src/**/*.test.*" + "dist" ], "exports": { ".": { "default": "./src/index.js" } }, - "dependencies": { - "@sveltejs/sv-utils": "latest", + "publishConfig": { + "access": "public", + "exports": { + ".": { + "default": "./dist/index.js" + } + } + }, + "peerDependencies": { "sv": "latest" }, "devDependencies": { "@playwright/test": "^1.58.2", + "@sveltejs/sv-utils": "latest", + "sv": "latest", + "tsdown": "^0.21.4", "vitest": "^4.1.0" }, "keywords": [ "sv-add" - ], - "publishConfig": { - "directory": "dist", - "access": "public" - } + ] } diff --git a/packages/sv/src/cli/tests/snapshots/@my-org/sv/tsdown.config.js b/packages/sv/src/cli/tests/snapshots/@my-org/sv/tsdown.config.js new file mode 100644 index 000000000..7f45e3cce --- /dev/null +++ b/packages/sv/src/cli/tests/snapshots/@my-org/sv/tsdown.config.js @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsdown'; + +export default defineConfig({ + entry: ['src/index.js'], + format: 'esm' +}); diff --git a/packages/sv/src/core/fetch-packages.ts b/packages/sv/src/core/fetch-packages.ts index 02e240c68..0952a72a8 100644 --- a/packages/sv/src/core/fetch-packages.ts +++ b/packages/sv/src/core/fetch-packages.ts @@ -14,22 +14,21 @@ import type { AddonDefinition, AddonReference } from './config.ts'; const NODE_MODULES = fileURLToPath(new URL('../../node_modules', import.meta.url)); function verifyPackage(addonPkg: Record, specifier: string): string | undefined { - // We should look only for dependencies, not devDependencies or peerDependencies + const peerDeps = { ...addonPkg.peerDependencies }; const deps = { ...addonPkg.dependencies }; - // valid addons should always have a dependency on `sv` - const addonSvVersion = deps['sv']; + // valid addons should always have `sv` as a peerDependency + const addonSvVersion = peerDeps['sv']; if (!addonSvVersion) { throw new Error( - `Invalid add-on package specified: '${specifier}' is missing a dependency on 'sv' in its 'package.json'` + `Invalid add-on package specified: '${specifier}' is missing 'sv' in its 'peerDependencies'` ); } - // addons should never have any external dependencies outside of `sv` and `@sveltejs/sv-utils` - for (const dep of Object.keys(deps)) { - if (dep === 'sv' || dep === '@sveltejs/sv-utils') continue; + // addons should not have any dependencies (everything should be bundled) + if (Object.keys(deps).length > 0) { throw new Error( - `Invalid add-on package detected: '${specifier}'\nCommunity addons should not have any external 'dependencies' besides 'sv'. Consider bundling your dependencies if they are necessary` + `Invalid add-on package detected: '${specifier}'\nCommunity add-ons should not have any 'dependencies'. Use 'peerDependencies' for 'sv' and bundle everything else` ); } diff --git a/packages/sv/src/create/shared/+addon/CONTRIBUTING.md b/packages/sv/src/create/shared/+addon/CONTRIBUTING.md index 20e629e3c..d5da26c73 100644 --- a/packages/sv/src/create/shared/+addon/CONTRIBUTING.md +++ b/packages/sv/src/create/shared/+addon/CONTRIBUTING.md @@ -24,15 +24,25 @@ Your `add-on` should: - export a function that returns a `defineAddon` object. - have a `package.json` with an `exports` field that points to the main entry point of the add-on. -## Sharing your add-on +## Building -When you're ready to publish your add-on to npm, run: +Your add-on is bundled with [tsdown](https://tsdown.dev/) into a single file in `dist/`. This bundles everything except `sv` (which is a peer dependency provided at runtime). -```shell +```sh +npm run build +``` + +## Publishing + +When you're ready to publish your add-on to npm: + +```sh npm login npm publish ``` +> `prepublishOnly` will automatically run the build before publishing. + ## Things to be aware of -Community add-ons are **not permitted** to have any external dependencies outside of `sv`. If the use of a dependency is absolutely necessary, then they can be bundled using a bundler of your choosing (e.g. Rollup, Rolldown, tsup, etc.). +Community add-ons must have `sv` as a `peerDependency` and should **not** have any `dependencies`. Everything else (including `@sveltejs/sv-utils`) is bundled at build time by tsdown. diff --git a/packages/sv/src/create/templates/addon/package.template.json b/packages/sv/src/create/templates/addon/package.template.json index 5e4bcf870..cfb672e72 100644 --- a/packages/sv/src/create/templates/addon/package.template.json +++ b/packages/sv/src/create/templates/addon/package.template.json @@ -8,26 +8,34 @@ "demo-create": "sv create demo --types ts --template minimal --no-add-ons --no-install", "demo-add": "sv add file:../ --cwd demo --no-git-check --no-install", "demo-add:ci": "sv add file:../=who:you --cwd demo --no-git-check --no-download-check --no-install", + "build": "tsdown", + "prepublishOnly": "npm run build", "test": "vitest run" }, - "files": ["src", "!src/**/*.test.*"], + "files": ["dist"], "exports": { ".": { "default": "./src/index.js" } }, - "dependencies": { - "@sveltejs/sv-utils": "workspace:*", + "publishConfig": { + "access": "public", + "exports": { + ".": { + "default": "./dist/index.js" + } + } + }, + "peerDependencies": { "sv": "workspace:*" }, "devDependencies": { + "sv": "workspace:*", + "@sveltejs/sv-utils": "workspace:*", "@playwright/test": "^1.58.2", "@types/node": "^25.2.1", + "tsdown": "^0.21.4", "vitest": "^4.1.0" }, - "keywords": ["sv-add"], - "publishConfig": { - "directory": "dist", - "access": "public" - } + "keywords": ["sv-add"] } diff --git a/packages/sv/src/create/templates/addon/tsdown.config.js b/packages/sv/src/create/templates/addon/tsdown.config.js new file mode 100644 index 000000000..7f45e3cce --- /dev/null +++ b/packages/sv/src/create/templates/addon/tsdown.config.js @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsdown'; + +export default defineConfig({ + entry: ['src/index.js'], + format: 'esm' +});