diff --git a/README.md b/README.md index 8e526b0..c69735e 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,14 @@ npx makes aurelia This will cause `npx` to download the `makes` tool, along with the `aurelia` scaffold from this repo, which it will use to guide you through creating your project. +## Presets and samples + +Use the preset picker to quickly choose a profile, including **Lean Modern Frontend** (TypeScript + Vite + Tailwind + Vitest + Storybook). When picking sample code, you can select **Blank app** for a clean, empty app shell (no demo markup) or use the minimal/router samples as before. + +## Plugin projects (Vite + Webpack) + +Plugin templates support Vite or Webpack. The Vite plugin build uses Vite's library mode (Rollup under the hood) and injects component CSS into the JS bundle so consumers don't need to import a separate CSS file. The dev-app still runs on the selected bundler for local testing. + ## Development There are some tests for this skeleton, setup in package.json. (totally not required by makes) diff --git a/__test__/after-task.spec.js b/__test__/after-task.spec.js index f549d21..598f32b 100644 --- a/__test__/after-task.spec.js +++ b/__test__/after-task.spec.js @@ -1,4 +1,7 @@ const test = require('ava'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); const after = require('../after'); const ansiColors = (m) => m; @@ -196,6 +199,48 @@ test('"after" task installs deps with pnpm, and prints summary', async t => { ); }); +test.serial('"after" task writes pnpm .npmrc when pnpm is selected', async t => { + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aurelia-pnpm-')); + const cwd = process.cwd(); + t.teardown(() => { + process.chdir(cwd); + fs.rmSync(tempDir, {recursive: true, force: true}); + }); + process.chdir(tempDir); + + const prompts = { + select(opts) { + t.deepEqual(opts.choices.map(c => c.value), ['npm', 'yarn', 'pnpm', undefined]); + return 'pnpm'; + } + }; + + function run(cmd, args) { + t.is(cmd, 'pnpm'); + t.deepEqual(args, ['install']); + } + + await after({ + unattended: false, + here: true, + prompts, + run, + properties: {name: 'my-app'}, + features: [], + notDefaultFeatures: [], + ansiColors + }, { + _isAvailable: isAvailable, + _log() {} + }); + + const npmrcPath = path.join(tempDir, '.npmrc'); + t.true(fs.existsSync(npmrcPath)); + const content = fs.readFileSync(npmrcPath, 'utf8'); + t.true(content.includes('shamefully-hoist=true')); + t.true(content.includes('auto-install-peers=true')); +}); + test('"after" task installs deps, and prints summary in here mode', async t => { const prompts = { select(opts) { diff --git a/__test__/before-task.spec.js b/__test__/before-task.spec.js index 19576d2..4f1ba5f 100644 --- a/__test__/before-task.spec.js +++ b/__test__/before-task.spec.js @@ -44,6 +44,50 @@ test('"before" task can select default-typescript preset', async t => { }); }); +test('"before" task can select minimal-esnext preset', async t => { + const prompts = { + select(opts) { + t.truthy(opts.choices.find(c => c.value === 'minimal-esnext')); + return 'minimal-esnext'; + } + }; + + const result = await before({unattended: false, prompts}); + t.deepEqual(result, { + silentQuestions: true, + preselectedFeatures: ['app', 'vite', 'babel', 'no-unit-tests', 'app-blank', 'css'] + }); +}); + +test('"before" task can select minimal-typescript preset', async t => { + const prompts = { + select(opts) { + t.truthy(opts.choices.find(c => c.value === 'minimal-typescript')); + return 'minimal-typescript'; + } + }; + + const result = await before({unattended: false, prompts}); + t.deepEqual(result, { + silentQuestions: true, + preselectedFeatures: ['app', 'vite', 'typescript', 'no-unit-tests', 'app-blank', 'css'] + }); +}); + +test('"before" task can select lean-modern-frontend preset', async t => { + const prompts = { + select(opts) { + t.truthy(opts.choices.find(c => c.value === 'lean-modern-frontend')); + return 'lean-modern-frontend'; + } + }; + + const result = await before({unattended: false, prompts}); + t.deepEqual(result, { + silentQuestions: true, + preselectedFeatures: ['app', 'vite', 'typescript', 'vitest', 'tailwindcss', 'storybook', 'app-min'] + }); +}); test('"before" task can select default-esnext-plugin preset', async t => { const prompts = { select(opts) { @@ -55,7 +99,7 @@ test('"before" task can select default-esnext-plugin preset', async t => { const result = await before({ unattended: false, prompts }); t.deepEqual(result, { silentQuestions: true, - preselectedFeatures: ['plugin', 'webpack', 'babel', 'shadow-dom', 'jest'] + preselectedFeatures: ['plugin', 'vite', 'babel', 'shadow-dom', 'vitest'] }); }); @@ -72,7 +116,7 @@ test('"before" task can select default-typescript-plugin preset', async t => { const result = await before({ unattended: false, prompts }); t.deepEqual(result, { silentQuestions: true, - preselectedFeatures: ['plugin', 'webpack', 'typescript', 'shadow-dom', 'jest'] + preselectedFeatures: ['plugin', 'vite', 'typescript', 'shadow-dom', 'vitest'] }); }); test('"before" task can select no preset', async t => { @@ -88,4 +132,3 @@ test('"before" task can select no preset', async t => { const result = await before({unattended: false, prompts}); t.is(result, undefined); }); - diff --git a/after.js b/after.js index c1db531..22ddf46 100644 --- a/after.js +++ b/after.js @@ -4,6 +4,11 @@ const {execSync} = require('child_process'); const fs = require('fs'); const path = require('path'); +const PNPM_NPMRC = `# for pnpm, use flat node_modules +shamefully-hoist=true +auto-install-peers=true +`; + function isAvailable(bin) { try { execSync(bin + ' -v', {stdio: 'ignore'}); @@ -23,6 +28,7 @@ module.exports = async function({ const c = ansiColors; let depsInstalled = false; let packageManager = undefined; + const projectDir = here ? '.' : properties.name; if (!unattended) { const choices = [ {value: 'npm', title: 'Yes, use npm'} ]; @@ -43,6 +49,9 @@ module.exports = async function({ }); if (packageManager) { + if (packageManager === 'pnpm') { + ensurePnpmrc(projectDir); + } await run(packageManager, ['install']); if (features.includes('playwright')) { @@ -107,3 +116,19 @@ module.exports = async function({ } _log((packageManager ?? 'npm') + ' start\n'); }; + +function ensurePnpmrc(projectDir) { + if (!projectDir || !fs.existsSync(projectDir)) return; + + const npmrcPath = path.join(projectDir, '.npmrc'); + if (!fs.existsSync(npmrcPath)) { + fs.writeFileSync(npmrcPath, PNPM_NPMRC); + return; + } + + const existing = fs.readFileSync(npmrcPath, 'utf8'); + if (existing.includes('shamefully-hoist=') && existing.includes('auto-install-peers=')) return; + + const spacer = existing.endsWith('\n') ? '' : '\n'; + fs.writeFileSync(npmrcPath, existing + spacer + PNPM_NPMRC); +} diff --git a/app-blank/src/my-app.ext b/app-blank/src/my-app.ext new file mode 100644 index 0000000..c6fa3a3 --- /dev/null +++ b/app-blank/src/my-app.ext @@ -0,0 +1,2 @@ +export class MyApp { +} diff --git a/app-blank/src/my-app.html b/app-blank/src/my-app.html new file mode 100644 index 0000000..c7423b4 --- /dev/null +++ b/app-blank/src/my-app.html @@ -0,0 +1 @@ +
Delete me
> \ No newline at end of file diff --git a/app-blank/src/my-app.stories.ext__if_storybook b/app-blank/src/my-app.stories.ext__if_storybook new file mode 100644 index 0000000..0d63baa --- /dev/null +++ b/app-blank/src/my-app.stories.ext__if_storybook @@ -0,0 +1,29 @@ +/* @if vite */ +import { MyApp } from './my-app'; + +const meta = { + title: 'Example/MyApp', + component: MyApp, + render: () => ({ + template: ``, + }) +}; + +export default meta; + +export const Default = {}; +/* @endif */ +/* @if webpack */ +import { MyApp } from './my-app'; + +export default { + title: 'MyApp', + component: MyApp, +}; + +export const Default = () => ({ + Component: MyApp, + template: '', + props: {} +}); +/* @endif */ diff --git a/app-blank/test__if_not_no-unit-tests/my-app.spec.ext b/app-blank/test__if_not_no-unit-tests/my-app.spec.ext new file mode 100644 index 0000000..5846bfe --- /dev/null +++ b/app-blank/test__if_not_no-unit-tests/my-app.spec.ext @@ -0,0 +1,20 @@ +// @if vitest +import { describe, it } from 'vitest'; +// @endif +import { MyApp } from '../src/my-app'; +import { createFixture } from '@aurelia/testing'; + +describe('my-app', () => { + it('should render', async () => { + const { appHost } = await createFixture( + '', + {}, + [MyApp], + ).started; + + const element = appHost.querySelector('my-app'); + if (element === null) { + throw new Error('Expected to find my-app element in host'); + } + }); +}); diff --git a/app-min/src/my-app.html b/app-min/src/my-app.html index 1ac1297..77edb08 100644 --- a/app-min/src/my-app.html +++ b/app-min/src/my-app.html @@ -1,12 +1,10 @@ -
-
-
Welcome to Aurelia 2!
-
You're now running ${message}
-
Experience the power of Aurelia 2 with TailwindCSS
-
Aurelia 2
+
+
+

${message}

+

Aurelia 2 + TailwindCSS

-
+
This content uses shared Shadow DOM styles!
diff --git a/app-with-router/src/about-page.html b/app-with-router/src/about-page.html index 371f79b..af63cfe 100644 --- a/app-with-router/src/about-page.html +++ b/app-with-router/src/about-page.html @@ -1,21 +1,7 @@ -
-

About

-
-

- This is a sample Aurelia application showcasing the integration of TailwindCSS with the Aurelia framework. - The combination provides a powerful development experience with utility-first CSS styling. -

-
-

Features

-
    -
  • • Aurelia 2 with modern web standards
  • -
  • • TailwindCSS for utility-first styling
  • -
  • • Responsive design out of the box
  • -
  • • Fast development workflow
  • -
-
-
+
+

About

+

This page is routed via @aurelia/router.

diff --git a/app-with-router/src/missing-page.ext b/app-with-router/src/missing-page.ext deleted file mode 100644 index ced926e..0000000 --- a/app-with-router/src/missing-page.ext +++ /dev/null @@ -1,3 +0,0 @@ -export class MissingPage { - /* @if typescript */public /* @endif */missingComponent/* @if typescript */: string /* @endif */ = 'Unknown page'; -} diff --git a/app-with-router/src/missing-page.html b/app-with-router/src/missing-page.html deleted file mode 100644 index 823482d..0000000 --- a/app-with-router/src/missing-page.html +++ /dev/null @@ -1,12 +0,0 @@ - -
-

404 - Page Not Found

-

Sorry, the page you're looking for doesn't exist.

- Go Home -
- - -

404 - Page Not Found

-

Sorry, the page you're looking for doesn't exist.

-Go Home - diff --git a/app-with-router/src/my-app.ext b/app-with-router/src/my-app.ext index 7fc5da3..39d8778 100644 --- a/app-with-router/src/my-app.ext +++ b/app-with-router/src/my-app.ext @@ -13,7 +13,6 @@ import { route } from '@aurelia/router'; title: 'About', }, ], - fallback: import('./missing-page'), }) export class MyApp { } diff --git a/app-with-router/src/my-app.html b/app-with-router/src/my-app.html index 904d578..b3f4d21 100644 --- a/app-with-router/src/my-app.html +++ b/app-with-router/src/my-app.html @@ -1,22 +1,14 @@
-