diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 32046f255..c773caded 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,13 +49,13 @@ jobs: node-version: [18, 20, 22] os: [ubuntu-latest, windows-latest, macos-latest] verification-script: - - pnpm --filter "\!*typescript*" build - - pnpm --filter "*typescript*" build - - pnpm --filter "*vitest*" test:unit - - pnpm --filter "*eslint*" lint --no-fix --max-warnings=0 - - pnpm --filter "*prettier*" format --write --check + - pnpm --filter '!*typescript*' build + - pnpm --filter '*typescript*' build + - pnpm --filter '*vitest*' test:unit + - pnpm --filter '*eslint*' lint --no-fix --max-warnings=0 + - pnpm --filter '*prettier*' format --write --check # FIXME: it's failing now - # - pnpm --filter "*with-tests*" test:unit + # - pnpm --filter '*with-tests*' test:unit runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.os == 'windows-latest' }} env: @@ -163,11 +163,12 @@ jobs: - name: Run build script working-directory: ./playground - run: pnpm --filter "*${{ matrix.e2e-framework }}*" build + run: pnpm --filter '*${{ matrix.e2e-framework }}*' build - name: Run e2e test script working-directory: ./playground - run: pnpm --filter "*${{ matrix.e2e-framework }}*" --workspace-concurrency 1 test:e2e + # bare templates can't pass e2e tests because their page structures don't match the example tests + run: pnpm --filter '*${{ matrix.e2e-framework }}*' --filter '!*bare*' --workspace-concurrency 1 test:e2e - name: Cypress component testing for projects without Vitest if: ${{ contains(matrix.e2e-framework, 'cypress') }} diff --git a/index.ts b/index.ts index 40f70304b..bf6031eb5 100755 --- a/index.ts +++ b/index.ts @@ -18,6 +18,7 @@ import generateReadme from './utils/generateReadme' import getCommand from './utils/getCommand' import getLanguage from './utils/getLanguage' import renderEslint from './utils/renderEslint' +import trimBoilerplate from './utils/trimBoilerplate' function isValidPackageName(projectName) { return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(projectName) @@ -83,7 +84,9 @@ async function init() { // --playwright // --eslint // --eslint-with-prettier (only support prettier through eslint for simplicity) - // --force (for force overwriting) + // in addition to the feature flags, you can also pass the following options: + // --bare (for a barebone template without example code) + // --force (for force overwriting without confirming) const args = process.argv.slice(2) @@ -319,8 +322,8 @@ async function init() { packageName = projectName ?? defaultProjectName, shouldOverwrite = argv.force, needsJsx = argv.jsx, - needsTypeScript = argv.ts || argv.typescript, - needsRouter = argv.router || argv['vue-router'], + needsTypeScript = (argv.ts || argv.typescript) as boolean, + needsRouter = (argv.router || argv['vue-router']) as boolean, needsPinia = argv.pinia, needsVitest = argv.vitest || argv.tests, needsPrettier = argv['eslint-with-prettier'], @@ -563,6 +566,25 @@ async function init() { ) } + if (argv.bare) { + trimBoilerplate(root, { needsTypeScript, needsRouter }) + render('bare/base') + + // TODO: refactor the `render` utility to avoid this kind of manual mapping? + if (needsTypeScript) { + render('bare/typescript') + } + if (needsVitest) { + render('bare/vitest') + } + if (needsCypressCT) { + render('bare/cypress-ct') + } + if (needsNightwatchCT) { + render('bare/nightwatch-ct') + } + } + // Instructions: // Supported package managers: pnpm > yarn > bun > npm const userAgent = process.env.npm_config_user_agent ?? '' diff --git a/scripts/snapshot.mjs b/scripts/snapshot.mjs index de5de0b0d..1b523a0fb 100644 --- a/scripts/snapshot.mjs +++ b/scripts/snapshot.mjs @@ -8,6 +8,7 @@ if (!/pnpm/.test(process.env.npm_config_user_agent ?? '')) throw new Error("Please use pnpm ('pnpm run snapshot') to generate snapshots!") const featureFlags = [ + 'bare', 'typescript', 'jsx', 'router', @@ -54,12 +55,7 @@ function fullCombination(arr) { } let flagCombinations = fullCombination(featureFlags) -flagCombinations.push( - ['default'], - ['router', 'pinia'], - ['eslint'], - ['eslint-with-prettier'], -) +flagCombinations.push(['default'], ['bare', 'default'], ['eslint'], ['eslint-with-prettier']) // `--with-tests` are equivalent of `--vitest --cypress` // Previously it means `--cypress` without `--vitest`. @@ -85,10 +81,15 @@ for (const flags of flagCombinations) { } // Filter out combinations that are not allowed -flagCombinations = flagCombinations.filter( - (combination) => - !featureFlagsDenylist.some((denylist) => denylist.every((flag) => combination.includes(flag))), -) +flagCombinations = flagCombinations + .filter( + (combination) => + !featureFlagsDenylist.some((denylist) => + denylist.every((flag) => combination.includes(flag)), + ), + ) + // `--bare` is a supplementary flag and should not be used alone + .filter((combination) => !(combination.length === 1 && combination[0] === 'bare')) const bin = path.posix.relative('../playground/', '../outfile.cjs') diff --git a/template/bare/base/src/App.vue b/template/bare/base/src/App.vue new file mode 100644 index 000000000..6ca279f5d --- /dev/null +++ b/template/bare/base/src/App.vue @@ -0,0 +1,7 @@ + + + + + diff --git a/template/bare/cypress-ct/src/__tests__/App.cy.js b/template/bare/cypress-ct/src/__tests__/App.cy.js new file mode 100644 index 000000000..55f8caa1b --- /dev/null +++ b/template/bare/cypress-ct/src/__tests__/App.cy.js @@ -0,0 +1,8 @@ +import App from '../App.vue' + +describe('App', () => { + it('mounts and renders properly', () => { + cy.mount(App) + cy.get('h1').should('contain', 'Hello World') + }) +}) diff --git a/template/bare/nightwatch-ct/src/__tests__/App.spec.js b/template/bare/nightwatch-ct/src/__tests__/App.spec.js new file mode 100644 index 000000000..86cd9e122 --- /dev/null +++ b/template/bare/nightwatch-ct/src/__tests__/App.spec.js @@ -0,0 +1,14 @@ +describe('App', function () { + before((browser) => { + browser.init() + }) + + it('mounts and renders properly', async function () { + const appComponent = await browser.mountComponent('/src/App.vue'); + + browser.expect.element(appComponent).to.be.present; + browser.expect.element('h1').text.to.contain('Hello World'); + }) + + after((browser) => browser.end()) +}) diff --git a/template/bare/typescript/src/App.vue b/template/bare/typescript/src/App.vue new file mode 100644 index 000000000..c2903a622 --- /dev/null +++ b/template/bare/typescript/src/App.vue @@ -0,0 +1,7 @@ + + + + + diff --git a/template/bare/vitest/src/__tests__/App.spec.js b/template/bare/vitest/src/__tests__/App.spec.js new file mode 100644 index 000000000..607fbfbab --- /dev/null +++ b/template/bare/vitest/src/__tests__/App.spec.js @@ -0,0 +1,11 @@ +import { describe, it, expect } from 'vitest' + +import { mount } from '@vue/test-utils' +import App from '../App.vue' + +describe('App', () => { + it('mounts renders properly', () => { + const wrapper = mount(App) + expect(wrapper.text()).toContain('Hello World') + }) +}) diff --git a/utils/trimBoilerplate.ts b/utils/trimBoilerplate.ts new file mode 100644 index 000000000..1a9fd704c --- /dev/null +++ b/utils/trimBoilerplate.ts @@ -0,0 +1,36 @@ +import * as fs from 'node:fs' +import * as path from 'path' + +function replaceContent(filepath: string, replacer: (content: string) => string) { + const content = fs.readFileSync(filepath, 'utf8') + fs.writeFileSync(filepath, replacer(content)) +} + +export default function trimBoilerplate(rootDir: string, features: Record) { + const isTs = features.needsTypeScript + const srcDir = path.resolve(rootDir, 'src') + + for (const filename of fs.readdirSync(srcDir)) { + // Keep `main.js/ts`, `router`, and `stores` directories + // `App.vue` would be re-rendered in the next step + if (['main.js', 'main.ts', 'router', 'stores'].includes(filename)) { + continue + } + const fullpath = path.resolve(srcDir, filename) + fs.rmSync(fullpath, { recursive: true }) + } + + // Remove CSS import in the entry file + const entryPath = path.resolve(rootDir, isTs ? 'src/main.ts' : 'src/main.js') + replaceContent(entryPath, (content) => content.replace("import './assets/main.css'\n\n", '')) + + // If `router` feature is selected, use an empty router configuration + if (features.needsRouter) { + const routerEntry = path.resolve(srcDir, isTs ? 'router/index.ts' : 'router/index.js') + replaceContent(routerEntry, (content) => + content + .replace(`import HomeView from '../views/HomeView.vue'\n`, '') + .replace(/routes:\s*\[[\s\S]*?\],/, 'routes: [],'), + ) + } +} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy