Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/sv-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
},
"keywords": [
"sv",
"sv-add",
"svelte",
"sveltekit"
]
Expand Down
8 changes: 8 additions & 0 deletions packages/sv/src/cli/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.`
Expand Down
5 changes: 3 additions & 2 deletions packages/sv/src/cli/tests/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
18 changes: 14 additions & 4 deletions packages/sv/src/cli/tests/snapshots/@my-org/sv/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
25 changes: 16 additions & 9 deletions packages/sv/src/cli/tests/snapshots/@my-org/sv/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from 'tsdown';

export default defineConfig({
entry: ['src/index.js'],
format: 'esm'
});
15 changes: 7 additions & 8 deletions packages/sv/src/core/fetch-packages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, any>, 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`
);
}

Expand Down
18 changes: 14 additions & 4 deletions packages/sv/src/create/shared/+addon/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
24 changes: 16 additions & 8 deletions packages/sv/src/create/templates/addon/package.template.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
}
6 changes: 6 additions & 0 deletions packages/sv/src/create/templates/addon/tsdown.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from 'tsdown';

export default defineConfig({
entry: ['src/index.js'],
format: 'esm'
});
Loading