ember-codemod-v1-to-v2 0.1.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 (36) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +129 -0
  3. package/bin/ember-codemod-v1-to-v2.js +42 -0
  4. package/package.json +67 -0
  5. package/src/blueprints/ember-addon/.prettierignore +25 -0
  6. package/src/blueprints/ember-addon/.prettierrc.js +5 -0
  7. package/src/blueprints/ember-addon/__addonLocation__/.eslintignore +8 -0
  8. package/src/blueprints/ember-addon/__addonLocation__/addon-main.js +5 -0
  9. package/src/blueprints/ember-addon/__addonLocation__/babel.config.json +8 -0
  10. package/src/blueprints/ember-addon/__addonLocation__/rollup.config.js +58 -0
  11. package/src/blueprints/ember-addon/__addonLocation__/rollup.config.mjs +55 -0
  12. package/src/blueprints/ember-addon/__addonLocation__/unpublished-development-types/index.d.ts +13 -0
  13. package/src/blueprints/ember-addon/__testAppLocation__/ember-cli-build.js +16 -0
  14. package/src/blueprints/ember-addon/__testAppLocation__/types/global.d.ts +7 -0
  15. package/src/blueprints/ember-addon/package.json +76 -0
  16. package/src/blueprints/ember-addon/pnpm-workspace.yaml +3 -0
  17. package/src/index.js +5 -0
  18. package/src/migration/ember-addon/index.js +41 -0
  19. package/src/migration/ember-addon/steps/analyze-addon.js +30 -0
  20. package/src/migration/ember-addon/steps/augment-options.js +111 -0
  21. package/src/migration/ember-addon/steps/create-files-from-blueprint.js +68 -0
  22. package/src/migration/ember-addon/steps/index.js +11 -0
  23. package/src/migration/ember-addon/steps/move-addon-files.js +79 -0
  24. package/src/migration/ember-addon/steps/move-project-root-files.js +126 -0
  25. package/src/migration/ember-addon/steps/move-test-app-files.js +103 -0
  26. package/src/migration/ember-addon/steps/update-addon-package-json.js +130 -0
  27. package/src/migration/ember-addon/steps/update-addon-tsconfig-json.js +42 -0
  28. package/src/migration/ember-addon/steps/update-test-app-package-json.js +85 -0
  29. package/src/migration/ember-addon/steps/update-test-app-tsconfig-json.js +47 -0
  30. package/src/migration/ember-addon/steps/use-relative-paths.js +88 -0
  31. package/src/migration/index.js +1 -0
  32. package/src/utils/convert-json-object.js +9 -0
  33. package/src/utils/decide-version.js +24 -0
  34. package/src/utils/files.js +88 -0
  35. package/src/utils/map-file-paths.js +20 -0
  36. package/src/utils/process-template.js +12 -0
@@ -0,0 +1,85 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ import {
5
+ convertToMap,
6
+ convertToObject,
7
+ } from '../../../utils/convert-json-object.js';
8
+ import { decideVersion } from '../../../utils/decide-version.js';
9
+
10
+ function moveDependenciesToDevDependencies(packageJson, options) {
11
+ const { packages } = options;
12
+
13
+ const dependencies = convertToMap(packageJson['dependencies']);
14
+ const devDependencies = convertToMap(packageJson['devDependencies']);
15
+
16
+ const packagesToMove = packages.addon.hasTypeScript
17
+ ? [
18
+ 'ember-auto-import',
19
+ 'ember-cli-babel',
20
+ 'ember-cli-htmlbars',
21
+ 'ember-cli-typescript',
22
+ ]
23
+ : ['ember-auto-import', 'ember-cli-babel', 'ember-cli-htmlbars'];
24
+
25
+ packagesToMove.forEach((packageName) => {
26
+ const version = decideVersion(packageName, dependencies);
27
+
28
+ devDependencies.set(packageName, version);
29
+
30
+ dependencies.delete(packageName);
31
+ });
32
+
33
+ packageJson['dependencies'] = convertToObject(dependencies);
34
+ packageJson['devDependencies'] = convertToObject(devDependencies);
35
+ }
36
+
37
+ function updateDependencies(packageJson) {
38
+ const dependencies = convertToMap(packageJson['dependencies']);
39
+
40
+ // May be easier to start over and add missing dependencies
41
+ dependencies.clear();
42
+
43
+ packageJson['dependencies'] = convertToObject(dependencies);
44
+ }
45
+
46
+ function updateDevDependencies(packageJson, options) {
47
+ const { packages } = options;
48
+
49
+ const devDependencies = convertToMap(packageJson['devDependencies']);
50
+
51
+ devDependencies.set(packages.addon.name, packages.addon.version);
52
+
53
+ packageJson['devDependencies'] = convertToObject(devDependencies);
54
+ }
55
+
56
+ function updateOtherFields(packageJson, options) {
57
+ const { packages } = options;
58
+
59
+ delete packageJson['ember-addon'];
60
+
61
+ packageJson['keywords'] = packageJson['keywords'].filter((keyword) => {
62
+ return keyword !== 'ember-addon';
63
+ });
64
+
65
+ packageJson['name'] = packages.testApp.name;
66
+
67
+ packageJson['private'] = true;
68
+ }
69
+
70
+ export function updateTestAppPackageJson(options) {
71
+ const { locations, projectRoot } = options;
72
+
73
+ const oldPath = join(projectRoot, locations.testApp, 'package.json');
74
+ const oldFile = readFileSync(oldPath, 'utf8');
75
+ const packageJson = JSON.parse(oldFile);
76
+
77
+ moveDependenciesToDevDependencies(packageJson, options);
78
+ updateDependencies(packageJson);
79
+ updateDevDependencies(packageJson, options);
80
+ updateOtherFields(packageJson, options);
81
+
82
+ const newFile = JSON.stringify(packageJson, null, 2) + '\n';
83
+
84
+ writeFileSync(oldPath, newFile, 'utf8');
85
+ }
@@ -0,0 +1,47 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ import {
5
+ convertToMap,
6
+ convertToObject,
7
+ } from '../../../utils/convert-json-object.js';
8
+
9
+ function updateCompilerOptions(tsconfigJson, options) {
10
+ const { packages } = options;
11
+
12
+ const compilerOptions = convertToMap(tsconfigJson['compilerOptions']);
13
+
14
+ compilerOptions.set('paths', {
15
+ [`${packages.testApp.name}/tests/*`]: ['tests/*'],
16
+ [`${packages.testApp.name}/*`]: ['app/*'],
17
+ '*': ['types/*'],
18
+ });
19
+
20
+ tsconfigJson['compilerOptions'] = convertToObject(compilerOptions);
21
+ }
22
+
23
+ function updateInclude(tsconfigJson) {
24
+ tsconfigJson['include'] = ['app/**/*', 'tests/**/*', 'types/**/*'];
25
+ }
26
+
27
+ export function updateTestAppTsconfigJson(options) {
28
+ const { locations, packages, projectRoot } = options;
29
+
30
+ if (!packages.addon.hasTypeScript) {
31
+ return;
32
+ }
33
+
34
+ const oldPath = join(projectRoot, locations.testApp, 'tsconfig.json');
35
+ const oldFile = readFileSync(oldPath, 'utf8');
36
+ const tsconfigJson = JSON.parse(
37
+ // Remove comments
38
+ oldFile.replace(new RegExp('//.*', 'mg'), '')
39
+ );
40
+
41
+ updateCompilerOptions(tsconfigJson, options);
42
+ updateInclude(tsconfigJson);
43
+
44
+ const newFile = JSON.stringify(tsconfigJson, null, 2) + '\n';
45
+
46
+ writeFileSync(oldPath, newFile, 'utf8');
47
+ }
@@ -0,0 +1,88 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join, relative } from 'node:path';
3
+
4
+ import glob from 'glob';
5
+
6
+ function normalizeRelativePath(relativePath) {
7
+ if (!relativePath.startsWith('..')) {
8
+ return `./${relativePath}`;
9
+ }
10
+
11
+ return relativePath;
12
+ }
13
+
14
+ function updateFile(oldFile, { filePath, projectName, projectRoot }) {
15
+ const regex = new RegExp(`\\b${projectName}/(.*/)*(.*)\\b`, 'g');
16
+ const matchResults = [...oldFile.matchAll(regex)];
17
+
18
+ let newFile = oldFile;
19
+
20
+ matchResults.forEach((matchResult) => {
21
+ const [magicImportPath, remainingDirectories, fileName] = matchResult;
22
+
23
+ const from = dirname(filePath);
24
+ const to = join(projectRoot, remainingDirectories);
25
+
26
+ const relativePath = join(relative(from, to), fileName);
27
+ const trueImportPath = normalizeRelativePath(relativePath);
28
+
29
+ newFile = newFile.replace(magicImportPath, trueImportPath);
30
+ });
31
+
32
+ return newFile;
33
+ }
34
+
35
+ function useRelativePathInAddonFolder(options) {
36
+ const { packages, projectRoot } = options;
37
+
38
+ // File extensions had been specified, partly to encode assumptions
39
+ // about Ember, and partly to avoid corrupting non-text files
40
+ const filePaths = glob.sync('addon/**/*.{d.ts,js,ts}', {
41
+ cwd: projectRoot,
42
+ dot: true,
43
+ nodir: true,
44
+ });
45
+
46
+ filePaths.forEach((filePath) => {
47
+ const oldPath = join(projectRoot, filePath);
48
+ const oldFile = readFileSync(oldPath, 'utf8');
49
+
50
+ const newFile = updateFile(oldFile, {
51
+ filePath,
52
+ projectName: packages.addon.name,
53
+ projectRoot: 'addon',
54
+ });
55
+
56
+ writeFileSync(oldPath, newFile, 'utf8');
57
+ });
58
+ }
59
+
60
+ function useRelativePathInTestsDummyFolder(options) {
61
+ const { projectRoot } = options;
62
+
63
+ // File extensions had been specified, partly to encode assumptions
64
+ // about Ember, and partly to avoid corrupting non-text files
65
+ const filePaths = glob.sync('tests/dummy/**/*.{d.ts,js,ts}', {
66
+ cwd: projectRoot,
67
+ dot: true,
68
+ nodir: true,
69
+ });
70
+
71
+ filePaths.forEach((filePath) => {
72
+ const oldPath = join(projectRoot, filePath);
73
+ const oldFile = readFileSync(oldPath, 'utf8');
74
+
75
+ const newFile = updateFile(oldFile, {
76
+ filePath,
77
+ projectName: 'dummy',
78
+ projectRoot: 'tests/dummy/app',
79
+ });
80
+
81
+ writeFileSync(oldPath, newFile, 'utf8');
82
+ });
83
+ }
84
+
85
+ export function useRelativePaths(options) {
86
+ useRelativePathInAddonFolder(options);
87
+ useRelativePathInTestsDummyFolder(options);
88
+ }
@@ -0,0 +1 @@
1
+ export * from './ember-addon/index.js';
@@ -0,0 +1,9 @@
1
+ export function convertToMap(object = {}) {
2
+ return new Map(Object.entries(object));
3
+ }
4
+
5
+ export function convertToObject(map) {
6
+ const sortedMap = new Map([...map.entries()].sort());
7
+
8
+ return Object.fromEntries(sortedMap);
9
+ }
@@ -0,0 +1,24 @@
1
+ const latestVersions = new Map([
2
+ ['@babel/core', '7.20.12'],
3
+ ['@babel/plugin-proposal-class-properties', '7.18.6'],
4
+ ['@babel/plugin-proposal-decorators', '7.20.7'],
5
+ ['@babel/preset-typescript', '7.18.6'],
6
+ ['@embroider/addon-dev', '3.0.0'],
7
+ ['@embroider/addon-shim', '1.8.4'],
8
+ ['@rollup/plugin-babel', '6.0.3'],
9
+ ['concurrently', '7.6.0'],
10
+ ['ember-auto-import', '2.5.0'],
11
+ ['ember-cli-babel', '7.26.11'],
12
+ ['ember-cli-htmlbars', '6.1.11'],
13
+ ['prettier', '2.8.3'],
14
+ ['rollup', '3.10.0'],
15
+ ['rollup-plugin-copy', '3.4.0'],
16
+ ['rollup-plugin-ts', '3.1.1'],
17
+ ]);
18
+
19
+ export function decideVersion(packageName, projectDependencies) {
20
+ const installedVersion = projectDependencies.get(packageName);
21
+ const latestVersion = `^${latestVersions.get(packageName)}`;
22
+
23
+ return installedVersion ?? latestVersion;
24
+ }
@@ -0,0 +1,88 @@
1
+ import {
2
+ copyFileSync,
3
+ existsSync,
4
+ mkdirSync,
5
+ readdirSync,
6
+ renameSync,
7
+ rmSync,
8
+ writeFileSync,
9
+ } from 'node:fs';
10
+ import { dirname, join } from 'node:path';
11
+
12
+ function removeDirectoryIfEmpty({ oldPath, projectRoot }) {
13
+ const directories = dirname(oldPath).split('/');
14
+ const depth = directories.length;
15
+
16
+ for (let i = 0; i < depth; i++) {
17
+ const directory = join(projectRoot, ...directories);
18
+ const numFilesLeft = readdirSync(directory).length;
19
+
20
+ if (numFilesLeft > 0) {
21
+ continue;
22
+ }
23
+
24
+ rmSync(directory, { recursive: true });
25
+ directories.pop();
26
+ }
27
+ }
28
+
29
+ export function copyFiles(pathMapping, options) {
30
+ const { projectRoot } = options;
31
+
32
+ pathMapping.forEach((newPath, oldPath) => {
33
+ const oldAbsolutePath = join(projectRoot, oldPath);
34
+
35
+ const newAbsolutePath = join(projectRoot, newPath);
36
+ const newDirectory = dirname(newAbsolutePath);
37
+
38
+ if (!existsSync(newDirectory)) {
39
+ mkdirSync(newDirectory, { recursive: true });
40
+ }
41
+
42
+ copyFileSync(oldAbsolutePath, newAbsolutePath);
43
+ });
44
+ }
45
+
46
+ export function createFiles(fileMapping, options) {
47
+ const { projectRoot } = options;
48
+
49
+ fileMapping.forEach((file, newPath) => {
50
+ const newAbsolutePath = join(projectRoot, newPath);
51
+ const newDirectory = dirname(newAbsolutePath);
52
+
53
+ if (!existsSync(newDirectory)) {
54
+ mkdirSync(newDirectory, { recursive: true });
55
+ }
56
+
57
+ writeFileSync(newAbsolutePath, file, 'utf8');
58
+ });
59
+ }
60
+
61
+ export function moveFiles(pathMapping, options) {
62
+ const { projectRoot } = options;
63
+
64
+ pathMapping.forEach((newPath, oldPath) => {
65
+ const oldAbsolutePath = join(projectRoot, oldPath);
66
+
67
+ const newAbsolutePath = join(projectRoot, newPath);
68
+ const newDirectory = dirname(newAbsolutePath);
69
+
70
+ if (!existsSync(newDirectory)) {
71
+ mkdirSync(newDirectory, { recursive: true });
72
+ }
73
+
74
+ renameSync(oldAbsolutePath, newAbsolutePath);
75
+ removeDirectoryIfEmpty({ oldPath, projectRoot });
76
+ });
77
+ }
78
+
79
+ export function removeFiles(pathMapping, options) {
80
+ const { projectRoot } = options;
81
+
82
+ pathMapping.forEach((newPath, oldPath) => {
83
+ const oldAbsolutePath = join(projectRoot, oldPath);
84
+
85
+ rmSync(oldAbsolutePath);
86
+ removeDirectoryIfEmpty({ oldPath, projectRoot });
87
+ });
88
+ }
@@ -0,0 +1,20 @@
1
+ function changeDirectory(oldPath, from, to) {
2
+ if (!oldPath.startsWith(from)) {
3
+ throw new RangeError(
4
+ `The provided path \`${oldPath}\` does not start with \`${from}\`.`
5
+ );
6
+ }
7
+
8
+ const relativePath = oldPath.replace(new RegExp(`^${from}/`), '');
9
+ const newPath = `${to}/${relativePath}`;
10
+
11
+ return [oldPath, newPath];
12
+ }
13
+
14
+ export function mapFilePaths(filePaths, directory) {
15
+ const { from, to } = directory;
16
+
17
+ return new Map(
18
+ filePaths.map((filePath) => changeDirectory(filePath, from, to))
19
+ );
20
+ }
@@ -0,0 +1,12 @@
1
+ /* https://github.com/ember-cli/ember-cli/blob/v4.9.2/lib/utilities/process-template.js */
2
+ import template from 'lodash.template';
3
+
4
+ export function processTemplate(content, context) {
5
+ const options = {
6
+ escape: /<%-([\s\S]+?)%>/g,
7
+ evaluate: /<%([\s\S]+?)%>/g,
8
+ interpolate: /<%=([\s\S]+?)%>/g,
9
+ };
10
+
11
+ return template(content, options)(context);
12
+ }