ember.nvp 1.14.0 → 1.16.0

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 (35) hide show
  1. package/.release-plan.json +37 -5
  2. package/CHANGELOG.md +31 -0
  3. package/README.md +28 -0
  4. package/package.json +2 -1
  5. package/packages/rolldown/package.json +2 -2
  6. package/packages/rolldown/src/transform.test.ts +3 -3
  7. package/packages/vite/package.json +4 -4
  8. package/packages/vite/src/ember.test.ts +38 -0
  9. package/packages/vite/src/ember.ts +38 -5
  10. package/src/bases/minimal-app/files/package.json +2 -2
  11. package/src/bases/minimal-extension/files/package.json +2 -2
  12. package/src/cli/generator.js +10 -0
  13. package/src/cli/index.js +19 -2
  14. package/src/cli/questions/layers.js +3 -2
  15. package/src/cli/questions/migration.js +69 -0
  16. package/src/index.js +1 -0
  17. package/src/layers/qunit/index.js +1 -1
  18. package/src/migration-from/addons.js +154 -0
  19. package/src/migration-from/app.js +1117 -0
  20. package/src/migration-from/at-ember/addon-blueprint/index.js +515 -0
  21. package/src/migration-from/at-ember/addon-blueprint/rollup.js +188 -0
  22. package/src/migration-from/at-ember/app-blueprint/index.js +60 -0
  23. package/src/migration-from/at-ember-tooling/classic-build-addon-blueprint/index.js +601 -0
  24. package/src/migration-from/at-ember-tooling/classic-build-app-blueprint/index.js +118 -0
  25. package/src/migration-from/babel.js +207 -0
  26. package/src/migration-from/ember-cli-build.js +238 -0
  27. package/src/migration-from/files.js +120 -0
  28. package/src/migration-from/findings.js +69 -0
  29. package/src/migration-from/index.js +81 -0
  30. package/src/migration-from/javascript.js +217 -0
  31. package/src/migration-from/layers.js +170 -0
  32. package/src/migration-from/library.js +311 -0
  33. package/src/utils/types.ts +69 -0
  34. package/test/ember.nvp/migration-from-fixtures.ts +506 -0
  35. package/test/ember.nvp/migration-from.test.ts +475 -0
@@ -0,0 +1,154 @@
1
+ import { existsSync, globSync } from "node:fs";
2
+ import { join, sep } from "node:path";
3
+ import { readJSON } from "#utils/fs.js";
4
+
5
+ /**
6
+ * @typedef {object} Dependency
7
+ * @property {string} name
8
+ * @property {Record<string, any>} manifest
9
+ */
10
+
11
+ /**
12
+ * Packages with the shape of a v1 addon that work without ember-cli
13
+ */
14
+ const WORKS_WITHOUT_EMBER_CLI = new Set(["@embroider/macros"]);
15
+
16
+ /**
17
+ * ember-cli runs v1 addons as part of its build.
18
+ * ember.nvp builds without ember-cli, so a v1 addon never loads.
19
+ *
20
+ * @param {Record<string, any>} manifest
21
+ */
22
+ export function isV1Addon(manifest) {
23
+ return (
24
+ Boolean(manifest.keywords?.includes("ember-addon")) &&
25
+ manifest["ember-addon"]?.version !== 2 &&
26
+ !WORKS_WITHOUT_EMBER_CLI.has(manifest.name)
27
+ );
28
+ }
29
+
30
+ /**
31
+ * The manifests of the dependencies that are installed in the project.
32
+ *
33
+ * A migration decides from the project's own files.
34
+ * An install is extra: when there is one, it tells which dependencies are v1 addons,
35
+ * and which modules the addons add to an app.
36
+ *
37
+ * Only the top level of node_modules is read.
38
+ * Every package manager puts a project's direct dependencies there.
39
+ *
40
+ * @param {string} directory the project
41
+ * @param {Record<string, string>} dependencies the dependencies to look for, by name
42
+ * @returns {Dependency[]} empty when the project has no node_modules
43
+ */
44
+ export function installedDependencies(directory, dependencies) {
45
+ let modules = join(directory, "node_modules");
46
+
47
+ if (!existsSync(modules)) return [];
48
+
49
+ /** @type {Dependency[]} */
50
+ let found = [];
51
+
52
+ for (let folder of globSync(["*", "@*/*"], { cwd: modules })) {
53
+ let name = folder.split(sep).join("/");
54
+
55
+ if (!(name in dependencies)) continue;
56
+
57
+ let manifest = readJSON(join(modules, folder, "package.json"));
58
+
59
+ if (manifest) found.push({ name, manifest });
60
+ }
61
+
62
+ return found.sort((a, b) => a.name.localeCompare(b.name));
63
+ }
64
+
65
+ /**
66
+ * @param {Dependency[]} dependencies
67
+ * @returns {string[]} such as `ember-power-select@6.0.1`
68
+ */
69
+ export function v1AddonsIn(dependencies) {
70
+ /** @type {string[]} */
71
+ let v1 = [];
72
+
73
+ for (let { name, manifest } of dependencies) {
74
+ if (isV1Addon(manifest)) v1.push(`${name}@${manifest.version}`);
75
+ }
76
+
77
+ return v1;
78
+ }
79
+
80
+ /**
81
+ * @typedef {object} AppModule
82
+ * @property {string} addon the package that provides it
83
+ * @property {string} path such as `services/session`
84
+ * @property {string | undefined} specifier the module to import it from
85
+ */
86
+
87
+ /**
88
+ * Modules that v2 addons merge into an app through `ember-addon.app-js`.
89
+ *
90
+ * `@embroider/compat` merges them.
91
+ * An ember.nvp app does not, so the app must register the ones it looks up by name.
92
+ *
93
+ * @param {Dependency[]} dependencies
94
+ * @returns {AppModule[]}
95
+ */
96
+ export function appModulesIn(dependencies) {
97
+ /** @type {AppModule[]} */
98
+ let modules = [];
99
+
100
+ for (let { name, manifest } of dependencies) {
101
+ let appJs = manifest["ember-addon"]?.["app-js"] ?? {};
102
+
103
+ for (let [appPath, file] of Object.entries(appJs)) {
104
+ modules.push({
105
+ addon: name,
106
+ path: appPath.replace(/^\.\//, "").replace(/\.\w+$/, ""),
107
+ specifier: exportedAs(name, manifest, String(file)),
108
+ });
109
+ }
110
+ }
111
+
112
+ return modules;
113
+ }
114
+
115
+ /**
116
+ * The specifier that reaches `target` through the package's `exports`.
117
+ *
118
+ * exports: { "./*": "./dist/*.js" }, target: ./dist/_app_/services/x.js
119
+ * → pkg/_app_/services/x
120
+ *
121
+ * @param {string} name
122
+ * @param {Record<string, any>} manifest
123
+ * @param {string} target a file in the package, such as `./dist/_app_/services/x.js`
124
+ * @returns {string | undefined}
125
+ */
126
+ function exportedAs(name, manifest, target) {
127
+ let exports = manifest.exports;
128
+
129
+ if (!exports) return `${name}/${target.replace(/^\.\//, "")}`;
130
+ if (typeof exports !== "object") return;
131
+
132
+ for (let [key, value] of Object.entries(exports)) {
133
+ let path = typeof value === "string" ? value : (value?.import ?? value?.default);
134
+
135
+ if (typeof path !== "string" || typeof key !== "string") continue;
136
+
137
+ if (!path.includes("*")) {
138
+ if (path === target) return `${name}/${key.replace(/^\.\//, "")}`;
139
+ continue;
140
+ }
141
+
142
+ let [before = "", after = ""] = path.split("*");
143
+
144
+ if (
145
+ target.startsWith(before) &&
146
+ target.endsWith(after) &&
147
+ target.length >= before.length + after.length
148
+ ) {
149
+ let matched = target.slice(before.length, target.length - after.length);
150
+
151
+ return `${name}/${key.replace(/^\.\//, "").replace("*", matched)}`;
152
+ }
153
+ }
154
+ }