@ember/app-blueprint 6.10.0-alpha.7 → 6.10.0-alpha.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.release-plan.json +6 -10
  2. package/CHANGELOG.md +23 -0
  3. package/README.md +43 -0
  4. package/conditional-files/minimal/app/app.ts +10 -0
  5. package/conditional-files/minimal/app/templates/application.gts +7 -0
  6. package/conditional-files/no-compat/_js_babel.config.mjs +47 -0
  7. package/conditional-files/no-compat/_ts_babel.config.mjs +55 -0
  8. package/conditional-files/no-compat/app/app.ts +18 -0
  9. package/conditional-files/no-compat/app/config/environment.ts +38 -0
  10. package/eslint.config.mjs +5 -0
  11. package/files/app/templates/application.gts +2 -2
  12. package/files/index.html +8 -5
  13. package/files/package.json +29 -30
  14. package/files/testem.cjs +2 -1
  15. package/files/tests/index.html +10 -3
  16. package/files/tests/test-helper.ts +3 -2
  17. package/files/vite.config.mjs +3 -3
  18. package/index.js +213 -3
  19. package/package.json +8 -3
  20. package/tests/fixtures/tests-js-no-compat-10/app/components/.gitkeep +0 -0
  21. package/tests/fixtures/tests-js-no-compat-10/app/components/sweet.gjs +3 -0
  22. package/tests/fixtures/tests-js-no-compat-10/app/router.js +11 -0
  23. package/tests/fixtures/tests-js-no-compat-10/app/routes/fancy.js +6 -0
  24. package/tests/fixtures/tests-js-no-compat-10/app/styles/app.css +3 -0
  25. package/tests/fixtures/tests-js-no-compat-10/app/templates/application.gjs +7 -0
  26. package/tests/fixtures/tests-js-no-compat-10/app/templates/fancy.gjs +9 -0
  27. package/tests/fixtures/tests-js-no-compat-10/tests/acceptance/index-test.js +26 -0
  28. package/tests/fixtures/tests-js-no-compat-10/tests/integration/components/sweet-test.gjs +28 -0
  29. package/tests/fixtures/tests-js-no-compat-10/tests/routes/fancy-test.js +11 -0
  30. package/tests/fixtures/tests-ts-no-compat-10/app/components/.gitkeep +0 -0
  31. package/tests/fixtures/tests-ts-no-compat-10/app/components/sweet.gts +3 -0
  32. package/tests/fixtures/tests-ts-no-compat-10/app/router.ts +11 -0
  33. package/tests/fixtures/tests-ts-no-compat-10/app/routes/fancy.ts +8 -0
  34. package/tests/fixtures/tests-ts-no-compat-10/app/styles/app.css +3 -0
  35. package/tests/fixtures/tests-ts-no-compat-10/app/templates/application.gts +7 -0
  36. package/tests/fixtures/tests-ts-no-compat-10/app/templates/fancy.gts +9 -0
  37. package/tests/fixtures/tests-ts-no-compat-10/tests/acceptance/index-test.ts +26 -0
  38. package/tests/fixtures/tests-ts-no-compat-10/tests/integration/components/sweet-test.gjs +28 -0
  39. package/tests/fixtures/tests-ts-no-compat-10/tests/routes/fancy-test.js +11 -0
  40. package/tests/minimal.test.mjs +120 -0
  41. package/tests/no-compat.test.mjs +181 -0
package/index.js CHANGED
@@ -24,6 +24,45 @@ function stringifyAndNormalize(contents) {
24
24
  * (see `conditional-files`)
25
25
  */
26
26
  const replacers = {
27
+ 'app/app.ts'(locals, contents) {
28
+ if (locals.minimal) {
29
+ let filePath = join(CONDITIONAL_FILES, 'minimal', 'app/app.ts');
30
+ let raw = readFileSync(filePath).toString();
31
+ return ejs.render(raw, locals);
32
+ } else if (locals.noCompat) {
33
+ let filePath = join(CONDITIONAL_FILES, 'no-compat', 'app/app.ts');
34
+ let raw = readFileSync(filePath).toString();
35
+ return ejs.render(raw, locals);
36
+ }
37
+
38
+ return ejs.render(contents, locals);
39
+ },
40
+ 'app/config/environment.ts'(locals, contents) {
41
+ if (locals.noCompat) {
42
+ let filePath = join(
43
+ CONDITIONAL_FILES,
44
+ 'no-compat',
45
+ 'app/config/environment.ts',
46
+ );
47
+ let raw = readFileSync(filePath).toString();
48
+ return ejs.render(raw, locals);
49
+ }
50
+
51
+ return ejs.render(contents, locals);
52
+ },
53
+ 'app/templates/application.gts'(locals, contents) {
54
+ if (locals.minimal) {
55
+ let filePath = join(
56
+ CONDITIONAL_FILES,
57
+ 'minimal',
58
+ 'app/templates/application.gts',
59
+ );
60
+ let raw = readFileSync(filePath).toString();
61
+ return ejs.render(raw, locals);
62
+ }
63
+
64
+ return ejs.render(contents, locals);
65
+ },
27
66
  'package.json'(...args) {
28
67
  return this.updatePackageJson(...args);
29
68
  },
@@ -37,7 +76,14 @@ const replacers = {
37
76
  },
38
77
  'babel.config.mjs'(locals) {
39
78
  let prefix = locals.typescript ? '_ts_' : '_js_';
40
- let filePath = join(CONDITIONAL_FILES, prefix + 'babel.config.mjs');
79
+
80
+ let filePath = join(
81
+ ...[
82
+ CONDITIONAL_FILES,
83
+ locals.noCompat && 'no-compat',
84
+ prefix + 'babel.config.mjs',
85
+ ].filter(Boolean),
86
+ );
41
87
 
42
88
  let raw = readFileSync(filePath).toString();
43
89
 
@@ -80,6 +126,8 @@ module.exports = {
80
126
  options.packageManager === 'pnpm' && '"--pnpm"',
81
127
  options.ciProvider && `"--ci-provider=${options.ciProvider}"`,
82
128
  options.typescript && `"--typescript"`,
129
+ options.minimal && `"--minimal"`,
130
+ options.noCompat && `"--no-compat"`,
83
131
  !options.emberData && `"--no-ember-data"`,
84
132
  !options.warpDrive && `"--no-warp-drive"`,
85
133
  ]
@@ -101,6 +149,37 @@ module.exports = {
101
149
  execBinPrefix = 'pnpm';
102
150
  }
103
151
 
152
+ let welcome = options.welcome;
153
+ let warpDrive = options.warpDrive ?? options.emberData;
154
+ let minimal = false;
155
+ let compat = true;
156
+ /**
157
+ * --minimal overrides compat/no-compat
158
+ */
159
+ if (options.minimal) {
160
+ minimal = true;
161
+ compat = false;
162
+
163
+ // Invert defaults
164
+ {
165
+ welcome = options.welcome = process.argv.includes('--welcome');
166
+ warpDrive =
167
+ options.emberData =
168
+ options.warpDrive =
169
+ process.argv.includes('--ember-data') ||
170
+ process.argv.includes('--warp-drive');
171
+ }
172
+ }
173
+
174
+ if (!minimal) {
175
+ if (options.noCompat || options.compat === false) {
176
+ compat = false;
177
+ }
178
+ }
179
+
180
+ let noCompat = !compat;
181
+ let notMinimal = !minimal;
182
+
104
183
  return {
105
184
  appDirectory: directoryForPackageName(name),
106
185
  name,
@@ -113,14 +192,18 @@ module.exports = {
113
192
  options.packageManager !== 'yarn' && options.packageManager !== 'pnpm',
114
193
  invokeScriptPrefix,
115
194
  execBinPrefix,
116
- welcome: options.welcome,
195
+ welcome,
117
196
  blueprint: 'app',
118
197
  blueprintOptions,
119
198
  lang: options.lang,
120
- warpDrive: options.warpDrive ?? options.emberData,
199
+ warpDrive: warpDrive,
121
200
  ciProvider: options.ciProvider,
122
201
  typescript: options.typescript,
123
202
  packageManager: options.packageManager ?? 'npm',
203
+ compat,
204
+ noCompat,
205
+ minimal,
206
+ notMinimal,
124
207
  };
125
208
  },
126
209
 
@@ -131,6 +214,10 @@ module.exports = {
131
214
 
132
215
  let files = this._super();
133
216
 
217
+ // Locals is where we calculate defaults and such.
218
+ // Let's not duplicate that work here
219
+ options = this.locals(options);
220
+
134
221
  if (options.ciProvider !== 'github') {
135
222
  files = files.filter((file) => file.indexOf('.github') < 0);
136
223
  }
@@ -150,6 +237,43 @@ module.exports = {
150
237
  files = files.filter((file) => !file.includes('services/.gitkeep'));
151
238
  }
152
239
 
240
+ if (options.noCompat) {
241
+ files = files.filter((file) => {
242
+ return (
243
+ !file.includes('ember-cli') &&
244
+ !file.includes('ember-cli-build.js') &&
245
+ !file.includes('controllers/') &&
246
+ !file.includes('config/environment.js') &&
247
+ !file.includes('config/optional-features') &&
248
+ !file.includes('config/targets') &&
249
+ !file.includes('app/helpers/')
250
+ );
251
+ });
252
+ }
253
+
254
+ if (options.minimal) {
255
+ files = files.filter((file) => {
256
+ return (
257
+ !file.includes('.github/') &&
258
+ !file.includes('.prettierignore') &&
259
+ !file.includes('README') &&
260
+ !file.includes('deprecation-workflow') &&
261
+ !file.includes('components/') &&
262
+ !file.includes('eslint.config') &&
263
+ !file.includes('prettierrc') &&
264
+ !file.includes('public/') &&
265
+ !file.includes('routes/') &&
266
+ !file.includes('services/') &&
267
+ !file.includes('stylelint') &&
268
+ !file.includes('styles/') &&
269
+ !file.includes('template-lintrc') &&
270
+ !file.includes('testem') &&
271
+ !file.includes('tests/') &&
272
+ !file.includes('watchman')
273
+ );
274
+ });
275
+ }
276
+
153
277
  this._files = files;
154
278
 
155
279
  return this._files;
@@ -182,6 +306,92 @@ module.exports = {
182
306
  updatePackageJson(options, content) {
183
307
  let contents = JSON.parse(content);
184
308
 
309
+ if (options.minimal) {
310
+ // Remove linting
311
+ {
312
+ delete contents.scripts['format'];
313
+ delete contents.scripts['lint'];
314
+ delete contents.scripts['lint:format'];
315
+ delete contents.scripts['lint:fix'];
316
+ delete contents.scripts['lint:js'];
317
+ delete contents.scripts['lint:js:fix'];
318
+ delete contents.scripts['lint:css'];
319
+ delete contents.scripts['lint:css:fix'];
320
+ delete contents.scripts['lint:hbs'];
321
+ delete contents.scripts['lint:hbs:fix'];
322
+
323
+ delete contents.devDependencies['@babel/eslint-parser'];
324
+ delete contents.devDependencies['@eslint/js'];
325
+ delete contents.devDependencies['concurrently'];
326
+ delete contents.devDependencies['ember-template-lint'];
327
+ delete contents.devDependencies['eslint'];
328
+ delete contents.devDependencies['eslint-config-prettier'];
329
+ delete contents.devDependencies['eslint-plugin-ember'];
330
+ delete contents.devDependencies['eslint-plugin-n'];
331
+ delete contents.devDependencies['eslint-plugin-qunit'];
332
+ delete contents.devDependencies['eslint-plugin-warp-drive'];
333
+ delete contents.devDependencies['globals'];
334
+ delete contents.devDependencies['prettier'];
335
+ delete contents.devDependencies['prettier-plugin-ember-template-tag'];
336
+ delete contents.devDependencies['stylelint'];
337
+ delete contents.devDependencies['stylelint-config-standard'];
338
+ delete contents.devDependencies['typescript-eslint'];
339
+ }
340
+ // Remove testing
341
+ {
342
+ delete contents.scripts['test'];
343
+ delete contents.devDependencies['@ember/test-helpers'];
344
+ delete contents.devDependencies['@ember/test-waiters'];
345
+ delete contents.devDependencies['ember-qunit'];
346
+ delete contents.devDependencies['qunit'];
347
+ delete contents.devDependencies['qunit-dom'];
348
+ delete contents.devDependencies['testem'];
349
+ }
350
+ // Extraneous / non-core deps.
351
+ // if folks go minimal, they know what they are doing
352
+ {
353
+ delete contents.devDependencies['ember-welcome-page'];
354
+ delete contents.devDependencies['tracked-built-ins'];
355
+ delete contents.devDependencies['ember-page-title'];
356
+ delete contents.devDependencies['ember-modifier'];
357
+ delete contents.devDependencies['ember-cli-deprecation-workflow'];
358
+ delete contents.devDependencies['ember-resolver'];
359
+ }
360
+ // common-in-the-vite-ecosystem alias
361
+ {
362
+ contents.scripts.dev = contents.scripts.start;
363
+ }
364
+
365
+ contents.devDependencies['ember-strict-application-resolver'] = '^0.1.0';
366
+ }
367
+ if (options.noCompat) {
368
+ contents.type = 'module';
369
+ contents.engines.node = '>= 24';
370
+ delete contents.directories;
371
+ delete contents.devDependencies['@ember/string'];
372
+ delete contents.devDependencies['@ember/optional-features'];
373
+ delete contents.devDependencies['@embroider/compat'];
374
+ delete contents.devDependencies['@embroider/config-meta-loader'];
375
+ // Users should use npx ember-cli instead
376
+ delete contents.devDependencies['ember-cli'];
377
+ delete contents.devDependencies['ember-cli-babel'];
378
+ delete contents.devDependencies['ember-load-initializers'];
379
+
380
+ // A nice feature of modern apps is using sub-path imports
381
+ // Why specify the whole app name, when you can use `#`?
382
+ contents.imports = {
383
+ '#app/*': './app/*',
384
+ '#config': './app/config/environment',
385
+ '#components/*': './app/components/*',
386
+ };
387
+
388
+ if (contents.scripts.test) {
389
+ contents.scripts.test =
390
+ 'vite build --mode development && testem ci --port 0';
391
+ contents.devDependencies['testem'] = '^3.17.0';
392
+ }
393
+ }
394
+
185
395
  return stringifyAndNormalize(sortPackageJson(contents));
186
396
  },
187
397
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ember/app-blueprint",
3
- "version": "6.10.0-alpha.7",
3
+ "version": "6.10.0-alpha.9",
4
4
  "description": "Blueprint for next generation of Ember apps",
5
5
  "keywords": [
6
6
  "ember-blueprint"
@@ -25,12 +25,13 @@
25
25
  "ejs": "^3.1.10",
26
26
  "ember-cli-string-utils": "^1.1.0",
27
27
  "lodash": "^4.17.21",
28
+ "sort-package-json": "^3.5.1",
28
29
  "walk-sync": "^3.0.0"
29
30
  },
30
31
  "devDependencies": {
31
32
  "@eslint/js": "^9.3.0",
32
33
  "concurrently": "^9.1.2",
33
- "ember-cli": "^6.3.1",
34
+ "ember-cli": "^6.9.1",
34
35
  "eslint": "9.x",
35
36
  "eslint-config-prettier": "^9.1.0",
36
37
  "execa": "^9.1.0",
@@ -42,7 +43,7 @@
42
43
  "release-plan": "^0.17.2",
43
44
  "strip-ansi": "^7.1.0",
44
45
  "tmp-promise": "^3.0.3",
45
- "vitest": "^4.0.0-beta.17"
46
+ "vitest": "^4.0.16"
46
47
  },
47
48
  "scripts": {
48
49
  "lint": "concurrently 'pnpm:lint:*(!fix)'",
@@ -50,6 +51,10 @@
50
51
  "lint:eslint": "eslint .",
51
52
  "lint:prettier": "prettier . --check",
52
53
  "format": "prettier . --write",
54
+ "new:app": "pnpm dlx ember-cli@latest new my-app --blueprint .",
55
+ "update:all": "pnpm dlx update-blueprint-deps --filter . ./files/package.json",
56
+ "update:alpha": "pnpm dlx update-blueprint-deps --ember-source alpha --ember-cli alpha --ember-data alpha ./files/package.json",
57
+ "update:beta": "pnpm dlx update-blueprint-deps --ember-source beta --ember-cli beta --ember-data beta ./files/package.json",
53
58
  "test": "vitest"
54
59
  }
55
60
  }
@@ -0,0 +1,3 @@
1
+ <template>
2
+ {{yield}}
3
+ </template>
@@ -0,0 +1,11 @@
1
+ import EmberRouter from '@embroider/router';
2
+ import config from 'test-app/config/environment';
3
+
4
+ export default class Router extends EmberRouter {
5
+ location = config.locationType;
6
+ rootURL = config.rootURL;
7
+ }
8
+
9
+ Router.map(function () {
10
+ this.route('fancy');
11
+ });
@@ -0,0 +1,6 @@
1
+ import Route from '@ember/routing/route';
2
+ import { service } from '@ember/service';
3
+
4
+ export default class FancyRoute extends Route {
5
+ @service router;
6
+ }
@@ -0,0 +1,3 @@
1
+ .purple {
2
+ color: rebeccapurple;
3
+ }
@@ -0,0 +1,7 @@
1
+ import { LinkTo } from '@ember/routing';
2
+
3
+ <template>
4
+ {{outlet}}
5
+
6
+ <LinkTo @route="fancy" data-test-a>Fancy</LinkTo>
7
+ </template>
@@ -0,0 +1,9 @@
1
+ import Sweet from '../components/sweet.gjs';
2
+
3
+ <template>
4
+ <Sweet />
5
+
6
+ <p class="purple">Fancy</p>
7
+
8
+ {{outlet}}
9
+ </template>
@@ -0,0 +1,26 @@
1
+ import { module, test } from 'qunit';
2
+ import { visit, currentURL, click } from '@ember/test-helpers';
3
+ import { setupApplicationTest } from 'test-app/tests/helpers';
4
+
5
+ module('Acceptance | index', function (hooks) {
6
+ setupApplicationTest(hooks);
7
+
8
+ test('visiting /', async function (assert) {
9
+ await visit('/');
10
+
11
+ assert.strictEqual(currentURL(), '/');
12
+
13
+ await click('[data-test-a]');
14
+
15
+ assert.strictEqual(currentURL(), '/fancy');
16
+
17
+ assert.dom('p').hasText('Fancy');
18
+
19
+ assert.dom('p.purple').hasStyle(
20
+ {
21
+ 'color': 'rgb(102, 51, 153)',
22
+ },
23
+ 'The text should be purple if the app styles are working correctly',
24
+ )
25
+ });
26
+ });
@@ -0,0 +1,28 @@
1
+ import { module, test } from 'qunit';
2
+ import { setupRenderingTest } from 'test-app/tests/helpers';
3
+ import { render } from '@ember/test-helpers';
4
+ import Sweet from 'test-app/components/sweet';
5
+
6
+ module('Integration | Component | sweet', function (hooks) {
7
+ setupRenderingTest(hooks);
8
+
9
+ test('it renders', async function (assert) {
10
+ // Updating values is achieved using autotracking, just like in app code. For example:
11
+ // class State { @tracked myProperty = 0; }; const state = new State();
12
+ // and update using state.myProperty = 1; await rerender();
13
+ // Handle any actions with function myAction(val) { ... };
14
+
15
+ await render(<template><Sweet /></template>);
16
+
17
+ assert.dom().hasText('');
18
+
19
+ // Template block usage:
20
+ await render(<template>
21
+ <Sweet>
22
+ template block text
23
+ </Sweet>
24
+ </template>);
25
+
26
+ assert.dom().hasText('template block text');
27
+ });
28
+ });
@@ -0,0 +1,11 @@
1
+ import { module, test } from 'qunit';
2
+ import { setupTest } from 'test-app/tests/helpers';
3
+
4
+ module('Unit | Route | fancy', function (hooks) {
5
+ setupTest(hooks);
6
+
7
+ test('it exists', function (assert) {
8
+ let route = this.owner.lookup('route:fancy');
9
+ assert.ok(route);
10
+ });
11
+ });
@@ -0,0 +1,3 @@
1
+ <template>
2
+ {{yield}}
3
+ </template>
@@ -0,0 +1,11 @@
1
+ import EmberRouter from '@embroider/router';
2
+ import config from 'test-app/config/environment';
3
+
4
+ export default class Router extends EmberRouter {
5
+ location = config.locationType;
6
+ rootURL = config.rootURL;
7
+ }
8
+
9
+ Router.map(function () {
10
+ this.route('fancy');
11
+ });
@@ -0,0 +1,8 @@
1
+ import Route from '@ember/routing/route';
2
+ import { service } from '@ember/service';
3
+
4
+ import type RouterService from '@ember/routing/router-service';
5
+
6
+ export default class FancyRoute extends Route {
7
+ @service declare router: RouterService;
8
+ }
@@ -0,0 +1,3 @@
1
+ .purple {
2
+ color: rebeccapurple;
3
+ }
@@ -0,0 +1,7 @@
1
+ import { LinkTo } from '@ember/routing';
2
+
3
+ <template>
4
+ {{outlet}}
5
+
6
+ <LinkTo @route="fancy" data-test-a>Fancy</LinkTo>
7
+ </template>
@@ -0,0 +1,9 @@
1
+ import Sweet from '../components/sweet.gts';
2
+
3
+ <template>
4
+ <Sweet />
5
+
6
+ <p class="purple">Fancy</p>
7
+
8
+ {{outlet}}
9
+ </template>
@@ -0,0 +1,26 @@
1
+ import { module, test } from 'qunit';
2
+ import { visit, currentURL, click } from '@ember/test-helpers';
3
+ import { setupApplicationTest } from 'test-app/tests/helpers';
4
+
5
+ module('Acceptance | index', function (hooks) {
6
+ setupApplicationTest(hooks);
7
+
8
+ test('visiting /', async function (assert) {
9
+ await visit('/');
10
+
11
+ assert.strictEqual(currentURL(), '/');
12
+
13
+ await click('[data-test-a]');
14
+
15
+ assert.strictEqual(currentURL(), '/fancy');
16
+
17
+ assert.dom('p').hasText('Fancy');
18
+
19
+ assert.dom('p.purple').hasStyle(
20
+ {
21
+ 'color': 'rgb(102, 51, 153)',
22
+ },
23
+ 'The text should be purple if the app styles are working correctly',
24
+ )
25
+ });
26
+ });
@@ -0,0 +1,28 @@
1
+ import { module, test } from 'qunit';
2
+ import { setupRenderingTest } from 'test-app/tests/helpers';
3
+ import { render } from '@ember/test-helpers';
4
+ import Sweet from 'test-app/components/sweet';
5
+
6
+ module('Integration | Component | sweet', function (hooks) {
7
+ setupRenderingTest(hooks);
8
+
9
+ test('it renders', async function (assert) {
10
+ // Updating values is achieved using autotracking, just like in app code. For example:
11
+ // class State { @tracked myProperty = 0; }; const state = new State();
12
+ // and update using state.myProperty = 1; await rerender();
13
+ // Handle any actions with function myAction(val) { ... };
14
+
15
+ await render(<template><Sweet /></template>);
16
+
17
+ assert.dom().hasText('');
18
+
19
+ // Template block usage:
20
+ await render(<template>
21
+ <Sweet>
22
+ template block text
23
+ </Sweet>
24
+ </template>);
25
+
26
+ assert.dom().hasText('template block text');
27
+ });
28
+ });
@@ -0,0 +1,11 @@
1
+ import { module, test } from 'qunit';
2
+ import { setupTest } from 'test-app/tests/helpers';
3
+
4
+ module('Unit | Route | fancy', function (hooks) {
5
+ setupTest(hooks);
6
+
7
+ test('it exists', function (assert) {
8
+ let route = this.owner.lookup('route:fancy');
9
+ assert.ok(route);
10
+ });
11
+ });