From 6662269a466f6dc89efb4d4af94edb9da11ec9a0 Mon Sep 17 00:00:00 2001 From: jycouet Date: Sun, 7 Dec 2025 22:28:54 +0100 Subject: [PATCH 1/3] move to template --- .changeset/config.json | 3 +- community-addon-template/.gitignore | 4 - community-addon-template/README.md | 58 ------ community-addon-template/package.json | 28 --- community-addon-template/src/index.js | 33 ---- .../tests/custom-addon.test.ts | 34 ---- community-addon-template/tests/setup/suite.ts | 173 ------------------ .../docs/20-commands/10-sv-create.md | 1 + eslint.config.js | 3 +- packages/sv/lib/cli/add/index.ts | 5 +- packages/sv/lib/cli/create.ts | 5 +- packages/sv/lib/create/index.ts | 6 +- .../sv/lib/create/shared/+addon/README.md | 49 +++++ .../sv/lib/create/templates/addon/.gitignore | 26 +++ .../sv/lib/create/templates/addon/.ignore | 3 + .../sv/lib/create/templates/addon/.meta.json | 4 + .../lib/create/templates/addon}/jsconfig.json | 0 .../templates/addon/package.template.json | 22 +++ .../lib/create/templates/addon/src/index.js | 45 +++++ .../addon/tests/custom-addon.test.ts | 28 +++ .../addon}/tests/fixtures/+page.svelte | 0 .../addon}/tests/fixtures/App.svelte | 0 .../templates/addon}/tests/setup/global.ts | 2 +- .../templates/addon/tests/setup/suite.ts | 129 +++++++++++++ .../create/templates/addon}/vitest.config.js | 1 - .../templates/demo/package.template.json | 2 +- .../templates/library/package.template.json | 2 +- .../templates/minimal/package.template.json | 2 +- pnpm-lock.yaml | 13 -- pnpm-workspace.yaml | 1 - vitest.config.ts | 3 +- 31 files changed, 323 insertions(+), 362 deletions(-) delete mode 100644 community-addon-template/.gitignore delete mode 100644 community-addon-template/README.md delete mode 100644 community-addon-template/package.json delete mode 100644 community-addon-template/src/index.js delete mode 100644 community-addon-template/tests/custom-addon.test.ts delete mode 100644 community-addon-template/tests/setup/suite.ts create mode 100644 packages/sv/lib/create/shared/+addon/README.md create mode 100644 packages/sv/lib/create/templates/addon/.gitignore create mode 100644 packages/sv/lib/create/templates/addon/.ignore create mode 100644 packages/sv/lib/create/templates/addon/.meta.json rename {community-addon-template => packages/sv/lib/create/templates/addon}/jsconfig.json (100%) create mode 100644 packages/sv/lib/create/templates/addon/package.template.json create mode 100644 packages/sv/lib/create/templates/addon/src/index.js create mode 100644 packages/sv/lib/create/templates/addon/tests/custom-addon.test.ts rename {community-addon-template => packages/sv/lib/create/templates/addon}/tests/fixtures/+page.svelte (100%) rename {community-addon-template => packages/sv/lib/create/templates/addon}/tests/fixtures/App.svelte (100%) rename {community-addon-template => packages/sv/lib/create/templates/addon}/tests/setup/global.ts (100%) create mode 100644 packages/sv/lib/create/templates/addon/tests/setup/suite.ts rename {community-addon-template => packages/sv/lib/create/templates/addon}/vitest.config.js (90%) diff --git a/.changeset/config.json b/.changeset/config.json index 581533f27..f2091dd84 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -6,6 +6,5 @@ "linked": [], "access": "public", "baseBranch": "main", - "updateInternalDependencies": "patch", - "ignore": ["community-addon-template"] + "updateInternalDependencies": "patch" } diff --git a/community-addon-template/.gitignore b/community-addon-template/.gitignore deleted file mode 100644 index a9e1699a5..000000000 --- a/community-addon-template/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules -temp -.outputs -.test-output diff --git a/community-addon-template/README.md b/community-addon-template/README.md deleted file mode 100644 index 20f69acae..000000000 --- a/community-addon-template/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# community-addon-template - -> [!IMPORTANT] -> Community add-ons are currently not supported. Please see [#184](https://github.com/sveltejs/cli/issues/184) for details. - -> [!IMPORTANT] -> This template's dependencies may not be up-to-date; be sure to update them to the latest! -> If you get stuck, check out the [implementations of official add-ons](https://github.com/sveltejs/cli/tree/main/packages/add-ons). - -The add-on template for community add-ons for [`sv`](https://github.com/sveltejs/cli). - -## Cloning the template - -Use [`degit`](https://github.com/Rich-Harris/degit) to clone the template: - -```shell -npx degit sveltejs/cli/community-addon-template addon-name -``` - -## Using the add-on - -To run the add-on, we'll first need a project to apply it to. - -Create the project with the following script: - -```shell -npm run create-temp -``` - -This will create a SvelteKit project in the `temp` directory. - -To execute the add-on, run: - -```shell -npm start -``` - -## Sharing your add-on - -When you're ready to publish your add-on to npm, run: - -```shell -npm publish -``` - -Your published add-on can now be used by anyone! - -To execute the newly published package with `sv`, run: - -```shell -npx sv add --community npm:addon-package-name -``` - -After that, feel free to open a pull request to [`sv`](https://github.com/sveltejs/cli) and add your add-on to the [community list](/community-addons/). - -## 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.). diff --git a/community-addon-template/package.json b/community-addon-template/package.json deleted file mode 100644 index 349963fe4..000000000 --- a/community-addon-template/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "community-addon-template", - "private": true, - "version": "0.0.0", - "type": "module", - "license": "MIT", - "scripts": { - "create-temp": "sv create temp --types ts --template minimal --no-add-ons --no-install", - "start": "sv add -C temp --community file:../", - "test": "vitest run" - }, - "files": [ - "src", - "!src/**/*.test.*" - ], - "exports": "./src/index.js", - "dependencies": { - "sv": "workspace:*" - }, - "devDependencies": { - "@playwright/test": "^1.56.1", - "vitest": "4.0.7" - }, - "keywords": [ - "svelte-add-on", - "sv" - ] -} diff --git a/community-addon-template/src/index.js b/community-addon-template/src/index.js deleted file mode 100644 index 3c2f5ee50..000000000 --- a/community-addon-template/src/index.js +++ /dev/null @@ -1,33 +0,0 @@ -import { defineAddon, defineAddonOptions, js, svelte, parseSvelte } from 'sv/core'; - -export const options = defineAddonOptions() - .add('demo', { - question: 'Do you want to use a demo?', - type: 'boolean', - default: false - }) - .build(); - -export default defineAddon({ - id: 'community-addon', - options, - setup: ({ kit, unsupported }) => { - if (!kit) unsupported('Requires SvelteKit'); - }, - run: ({ sv, options, typescript }) => { - sv.file('addon-template-demo.txt', (content) => { - if (options.demo) { - return 'This is a text file made by the Community Addon Template demo!'; - } - return content; - }); - - sv.file('src/DemoComponent.svelte', (content) => { - if (!options.demo) return content; - const { ast, generateCode } = parseSvelte(content); - const scriptAst = svelte.ensureScript(ast, { langTs: typescript }); - js.imports.addDefault(scriptAst, { from: '../addon-template-demo.txt?raw', as: 'demo' }); - return generateCode(); - }); - } -}); diff --git a/community-addon-template/tests/custom-addon.test.ts b/community-addon-template/tests/custom-addon.test.ts deleted file mode 100644 index 8cc944167..000000000 --- a/community-addon-template/tests/custom-addon.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import path from 'node:path'; -import { expect } from '@playwright/test'; -import { fixture, setupTest } from './setup/suite.js'; -import addon from '../src/index.js'; - -const id = addon.id; -const { test, addonTestCases, prepareServer } = setupTest( - { [id]: addon }, - { kinds: [{ type: 'default', options: { [id]: { demo: true } } }] } -); - -test.concurrent.for(addonTestCases)( - 'community-addon $variant', - async (addonTestCase, { page, ...ctx }) => { - const cwd = ctx.cwd(addonTestCase); - - // ...add files - if (addonTestCase.variant.startsWith('kit')) { - const target = path.resolve(cwd, 'src', 'routes', '+page.svelte'); - fixture({ name: '+page.svelte', target }); - } else { - const target = path.resolve(cwd, 'src', 'App.svelte'); - fixture({ name: 'App.svelte', target }); - } - - const { close } = await prepareServer({ cwd, page }); - // kill server process when we're done - ctx.onTestFinished(async () => await close()); - - // expectations - const textContent = await page.getByTestId('demo').textContent(); - expect(textContent).toContain('This is a text file made by the Community Addon Template demo!'); - } -); diff --git a/community-addon-template/tests/setup/suite.ts b/community-addon-template/tests/setup/suite.ts deleted file mode 100644 index 962f3d776..000000000 --- a/community-addon-template/tests/setup/suite.ts +++ /dev/null @@ -1,173 +0,0 @@ -import fs from 'node:fs'; -import path from 'node:path'; -import { execSync } from 'node:child_process'; -import * as vitest from 'vitest'; -import { installAddon, type AddonMap, type OptionMap } from 'sv'; -import { - createProject, - startPreview, - addPnpmBuildDependencies, - type CreateProject, - type ProjectVariant -} from 'sv/testing'; -import { chromium, type Browser, type BrowserContext, type Page } from '@playwright/test'; -import { fileURLToPath } from 'node:url'; - -const cwd = vitest.inject('testDir'); -const templatesDir = vitest.inject('templatesDir'); -const variants = vitest.inject('variants'); - -const SETUP_DIR = fileURLToPath(new URL('.', import.meta.url)); - -type Fixtures = { - page: Page; - cwd(addonTestCase: AddonTestCase): string; -}; - -type AddonTestCase = { - variant: ProjectVariant; - kind: { type: string; options: OptionMap }; -}; - -export function setupTest( - addons: Addons, - options?: { - kinds: Array['kind']>; - filter?: (addonTestCase: AddonTestCase) => boolean; - browser?: boolean; - } -) { - const test = vitest.test.extend({} as any); - - const withBrowser = options?.browser ?? true; - - let create: CreateProject; - let browser: Browser; - - if (withBrowser) { - vitest.beforeAll(async () => { - browser = await chromium.launch(); - return async () => { - await browser.close(); - }; - }); - } - - const addonTestCases: Array> = []; - for (const kind of options?.kinds ?? []) { - for (const variant of variants) { - const addonTestCase = { variant, kind }; - if (!options?.filter || options?.filter?.(addonTestCase)) { - addonTestCases.push(addonTestCase); - } - } - } - let testName: string; - vitest.beforeAll(async ({ name }) => { - testName = path.dirname(name).split('/').at(-1)!; - - // constructs a builder for create test projects - create = createProject({ cwd, templatesDir, testName }); - - // creates a pnpm workspace in each addon dir - fs.writeFileSync( - path.resolve(cwd, testName, 'pnpm-workspace.yaml'), - "packages:\n - '**/*'", - 'utf8' - ); - - // creates a barebones package.json in each addon dir - fs.writeFileSync( - path.resolve(cwd, testName, 'package.json'), - JSON.stringify({ - name: `${testName}-workspace-root`, - private: true - }) - ); - - for (const { variant, kind } of addonTestCases) { - const cwd = create({ testId: `${kind.type}-${variant}`, variant }); - - // test metadata - const metaPath = path.resolve(cwd, 'meta.json'); - fs.writeFileSync(metaPath, JSON.stringify({ variant, kind }, null, '\t'), 'utf8'); - - const { pnpmBuildDependencies } = await installAddon({ - cwd, - addons, - options: kind.options, - packageManager: 'pnpm' - }); - await addPnpmBuildDependencies(cwd, 'pnpm', ['esbuild', ...pnpmBuildDependencies]); - } - - execSync('pnpm install', { cwd: path.resolve(cwd, testName), stdio: 'pipe' }); - }); - - // runs before each test case - vitest.beforeEach(async (ctx) => { - let browserCtx: BrowserContext; - if (withBrowser) { - browserCtx = await browser.newContext(); - ctx.page = await browserCtx.newPage(); - } - - ctx.cwd = (addonTestCase) => { - return path.join(cwd, testName, `${addonTestCase.kind.type}-${addonTestCase.variant}`); - }; - - return async () => { - if (withBrowser) { - await browserCtx.close(); - } - // ...other tear downs - }; - }); - - return { test, addonTestCases, prepareServer }; -} - -type PrepareServerOptions = { - cwd: string; - page: Page; - buildCommand?: string; - previewCommand?: string; -}; -// installs dependencies, builds the project, and spins up the preview server -async function prepareServer({ - cwd, - page, - buildCommand = 'pnpm build', - previewCommand = 'pnpm preview' -}: PrepareServerOptions) { - // build project - if (buildCommand) execSync(buildCommand, { cwd, stdio: 'pipe' }); - - // start preview server - const { url, close } = await startPreview({ cwd, command: previewCommand }); - - // increases timeout as 30s is not always enough when running the full suite - page.setDefaultNavigationTimeout(60_000); - - try { - // navigate to the page - await page.goto(url); - } catch (e) { - // cleanup in the instance of a timeout - await close(); - throw e; - } - - return { url, close }; -} - -/** - * Applies a fixture to the target path - */ -export function fixture({ name, target }: { name: string; target: string }) { - const fixturePath = path.resolve(SETUP_DIR, '..', 'fixtures', name); - if (!fs.existsSync(fixturePath)) { - throw new Error(`Fixture does not exist at: ${fixturePath}`); - } - fs.copyFileSync(fixturePath, target); -} diff --git a/documentation/docs/20-commands/10-sv-create.md b/documentation/docs/20-commands/10-sv-create.md index 2ac49088c..24ca665a0 100644 --- a/documentation/docs/20-commands/10-sv-create.md +++ b/documentation/docs/20-commands/10-sv-create.md @@ -29,6 +29,7 @@ Which project template to use: - `minimal` — barebones scaffolding for your new app - `demo` — showcase app with a word guessing game that works without JavaScript - `library` — template for a Svelte library, set up with `svelte-package` +- `addon` — template for a community add-on, ready to be tested & published ### `--types