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/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