@react-spectrum/codemods 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.
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.addMacroSupport = addMacroSupport;
7
+ const path = require('path');
8
+ const installPackage_1 = __importDefault(require("./installPackage"));
9
+ const logger_1 = __importDefault(require("./logger"));
10
+ async function addMacroSupport() {
11
+ const packageJson = require(path.join(process.cwd(), 'package.json'));
12
+ const parcelVersion = packageJson && (packageJson.dependencies?.parcel || packageJson.devDependencies?.parcel);
13
+ if (parcelVersion) {
14
+ logger_1.default.success('Parcel detected in package.json. Macros are supported by default in v2.12.0 and newer.');
15
+ return { isMacroPluginInstalled: false, isMacroSupportEnabled: false };
16
+ }
17
+ let isMacroPluginInstalled = await (0, installPackage_1.default)('unplugin-parcel-macros', { dev: true });
18
+ // TODO: Try to automatically update bundle config
19
+ return { isMacroPluginInstalled, isMacroSupportEnabled: false };
20
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = installPackage;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const logger_js_1 = __importDefault(require("./logger.js"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const execa = require('execa');
12
+ function detectPackageManager() {
13
+ let packageManagers = [
14
+ { name: 'yarn', file: 'yarn.lock', installCommand: 'add' },
15
+ { name: 'npm', file: 'package-lock.json', installCommand: 'install' },
16
+ { name: 'pnpm', file: 'pnpm-lock.yaml', installCommand: 'add' }
17
+ ];
18
+ for (let pm of packageManagers) {
19
+ if (fs_1.default.existsSync(pm.file)) {
20
+ return pm;
21
+ }
22
+ }
23
+ return null;
24
+ }
25
+ function hasPackageJson() {
26
+ return fs_1.default.existsSync(path_1.default.join(process.cwd(), 'package.json'));
27
+ }
28
+ async function installPackage(packageName, options) {
29
+ logger_js_1.default.info('Checking for package.json...');
30
+ if (!hasPackageJson()) {
31
+ logger_js_1.default.warn(`Could not find package.json in the current directory. Please install ${chalk_1.default.bold(packageName)} manually.\n`);
32
+ return false;
33
+ }
34
+ let packageManager = detectPackageManager();
35
+ if (!packageManager) {
36
+ logger_js_1.default.warn(`Could not detect package manager. Please install ${chalk_1.default.bold(packageName)} manually.\n`);
37
+ return false;
38
+ }
39
+ try {
40
+ logger_js_1.default.info(`Installing ${chalk_1.default.bold(packageName)} using ${chalk_1.default.bold(packageManager.name)}...`);
41
+ await execa(packageManager.name, [packageManager.installCommand, `${packageName}@latest`, options?.dev ? '-D' : undefined]);
42
+ logger_js_1.default.success(`Successfully installed ${chalk_1.default.bold(packageName)}!\n`);
43
+ return true;
44
+ }
45
+ catch (e) {
46
+ logger_js_1.default.warn(`Failed to install ${chalk_1.default.bold(packageName)} with ${chalk_1.default.bold(packageManager.name)}.\n\nReceived error: ${e.message}.\n\nPlease install ${chalk_1.default.bold(packageName)} manually.\n`);
47
+ return false;
48
+ }
49
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const chalk_1 = __importDefault(require("chalk"));
7
+ function info(message) {
8
+ console.log(chalk_1.default.cyan(message));
9
+ }
10
+ function success(message) {
11
+ console.log(chalk_1.default.green(`✓ ${message}`));
12
+ }
13
+ function warn(message) {
14
+ console.log(chalk_1.default.yellow(`WARNING: ${message}`));
15
+ }
16
+ function error(message) {
17
+ console.error(chalk_1.default.red(`ERROR: ${message}`));
18
+ }
19
+ exports.default = {
20
+ info,
21
+ success,
22
+ warn,
23
+ error
24
+ };
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.waitForKeypress = waitForKeypress;
4
+ async function waitForKeypress() {
5
+ await new Promise((resolve) => {
6
+ process.stdin.once('data', () => {
7
+ resolve();
8
+ });
9
+ });
10
+ }
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = transformer;
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ function areSpecifiersAlphabetized(specifiers) {
7
+ const specifierNames = specifiers.map((specifier) => specifier.imported.name);
8
+ const sortedNames = [...specifierNames].sort();
9
+ return specifierNames.join() === sortedNames.join();
10
+ }
11
+ /**
12
+ * Replaces individual package imports with monopackage imports, where possible.
13
+ *
14
+ * Works for:
15
+ * - `@react-spectrum/*` -> `@adobe/react-spectrum`.
16
+ * - `@react-aria/*` -> `react-aria`.
17
+ * - `@react-stately/*` -> `react-stately`.
18
+ *
19
+ * By default this will apply to all the above packages, or optionally you can specify which packages to apply this by passing a comma-separated list to the packages option: `--packages=react-aria,react-stately,react-spectrum`.
20
+ *
21
+ * Run this from a directory where the relevant packages are installed in node_modules so it knows which monopackage exports are available to use (since exports may vary by version).
22
+ */
23
+ function transformer(file, api, options) {
24
+ const j = api.jscodeshift;
25
+ const root = j(file.source);
26
+ const packages = {
27
+ 'react-spectrum': {
28
+ monopackage: '@adobe/react-spectrum',
29
+ individualPrefix: '@react-spectrum/'
30
+ },
31
+ 'react-aria': {
32
+ monopackage: 'react-aria',
33
+ individualPrefix: '@react-aria/'
34
+ },
35
+ 'react-stately': {
36
+ monopackage: 'react-stately',
37
+ individualPrefix: '@react-stately/'
38
+ }
39
+ };
40
+ const selectedPackages = options?.packages?.split(',').filter((pkg) => packages[pkg]) ||
41
+ Object.keys(packages);
42
+ let anyIndexFound = false;
43
+ const monopackageExports = {};
44
+ selectedPackages.forEach((pkg) => {
45
+ const indexPath = path.join(process.cwd(), `node_modules/${packages[pkg].monopackage}/dist/types.d.ts`);
46
+ if (fs.existsSync(indexPath)) {
47
+ anyIndexFound = true;
48
+ const indexFile = fs.readFileSync(indexPath, 'utf8');
49
+ const indexRoot = j(indexFile);
50
+ monopackageExports[pkg] = [];
51
+ // Collect all named exports from the monopackage index file
52
+ indexRoot.find(j.ExportNamedDeclaration).forEach((path) => {
53
+ path.node.specifiers?.forEach((specifier) => {
54
+ monopackageExports[pkg].push(specifier.exported.name);
55
+ });
56
+ });
57
+ // Collect all exports defined in export statements like "export { Component } from '...' "
58
+ indexRoot
59
+ .find(j.ExportNamedDeclaration, {
60
+ source: {
61
+ type: 'Literal'
62
+ }
63
+ })
64
+ .forEach((path) => {
65
+ path.node.specifiers?.forEach((specifier) => {
66
+ monopackageExports[pkg].push(specifier.exported.name);
67
+ });
68
+ });
69
+ }
70
+ else if (options?.packages?.split(',').includes(pkg)) {
71
+ console.warn(`The index file for ${packages[pkg].monopackage} at ${indexPath} does not exist. Ensure that ${packages[pkg].monopackage} is installed.`);
72
+ }
73
+ });
74
+ if (!anyIndexFound) {
75
+ console.warn('None of the index files for the selected packages exist. Ensure that the packages are installed.');
76
+ return root.toSource();
77
+ }
78
+ selectedPackages.forEach((pkg) => {
79
+ // Find all imports from individual packages
80
+ const individualPackageImports = root
81
+ .find(j.ImportDeclaration)
82
+ .filter((path) => {
83
+ return path.node.source.value?.startsWith(packages[pkg].individualPrefix);
84
+ });
85
+ if (individualPackageImports.size() === 0) {
86
+ return;
87
+ }
88
+ // Collect all imported specifiers from individual packages that are also in the monopackage exports
89
+ const importedSpecifiers = individualPackageImports
90
+ .nodes()
91
+ .reduce((acc, node) => {
92
+ node.specifiers?.forEach((specifier) => {
93
+ if (monopackageExports[pkg].includes(specifier.imported.name)) {
94
+ acc.push(specifier);
95
+ }
96
+ });
97
+ return acc;
98
+ }, []);
99
+ // Remove the old imports if they are present in monopackage exports
100
+ individualPackageImports.forEach((path) => {
101
+ path.node.specifiers = path.node.specifiers?.filter((specifier) => !monopackageExports[pkg].includes(specifier.imported.name));
102
+ });
103
+ // Remove import declarations with no specifiers left
104
+ individualPackageImports
105
+ .filter((path) => path.node.specifiers?.length === 0)
106
+ .remove();
107
+ // Find existing monopackage import if it exists
108
+ const monopackageImport = root.find(j.ImportDeclaration).filter((path) => {
109
+ return path.node.source.value === packages[pkg].monopackage;
110
+ });
111
+ if (monopackageImport.size() > 0) {
112
+ const existingImport = monopackageImport.at(0).get().node;
113
+ const specifiers = existingImport.specifiers;
114
+ if (areSpecifiersAlphabetized(specifiers)) {
115
+ // If imports are sorted, add the new import in sorted order
116
+ importedSpecifiers.forEach((newSpecifier) => {
117
+ const index = specifiers.findIndex((specifier) => specifier.imported.name > newSpecifier.imported.name);
118
+ if (index === -1) {
119
+ specifiers.push(newSpecifier);
120
+ }
121
+ else {
122
+ specifiers.splice(index, 0, newSpecifier);
123
+ }
124
+ });
125
+ }
126
+ else {
127
+ // If imports are not sorted, add the new import to the end
128
+ specifiers.push(...importedSpecifiers);
129
+ }
130
+ }
131
+ else if (importedSpecifiers.length > 0) {
132
+ // Create a new monopackage import with the collected specifiers
133
+ const newImport = j.importDeclaration(importedSpecifiers, j.literal(packages[pkg].monopackage));
134
+ // Insert the new import below the last existing import
135
+ const lastImport = root.find(j.ImportDeclaration).at(-1);
136
+ if (lastImport.size() > 0) {
137
+ lastImport.insertAfter(newImport);
138
+ }
139
+ else {
140
+ root.get().node.program.body.unshift(newImport);
141
+ }
142
+ }
143
+ });
144
+ return root.toSource();
145
+ }
146
+ transformer.parser = 'tsx';
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.use_monopackages = use_monopackages;
7
+ const Runner_js_1 = require("jscodeshift/src/Runner.js");
8
+ const path_1 = __importDefault(require("path"));
9
+ const transformPath = path_1.default.join(__dirname, 'codemod.js');
10
+ async function use_monopackages(options) {
11
+ let { path: filePath = '.', ...rest } = options;
12
+ return await (0, Runner_js_1.run)(transformPath, [filePath], rest);
13
+ }
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@react-spectrum/codemods",
3
+ "version": "0.1.0",
4
+ "main": "dist/index.js",
5
+ "source": "src/index.ts",
6
+ "bin": "dist/index.js",
7
+ "targets": {
8
+ "main": false
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "prepublishOnly": "yarn build"
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "type": "commonjs",
18
+ "license": "Apache-2.0",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/adobe/react-spectrum"
22
+ },
23
+ "dependencies": {
24
+ "@babel/parser": "^7.24.5",
25
+ "@babel/traverse": "^7.24.5",
26
+ "@babel/types": "^7.24.5",
27
+ "@react-spectrum/s2": "^0.3.0",
28
+ "@react-types/shared": "^3.24.0",
29
+ "@types/node": "^20",
30
+ "boxen": "^5.1.2",
31
+ "build": "^0.1.4",
32
+ "chalk": "^4.0.0",
33
+ "execa": "^5.1.1",
34
+ "jscodeshift": "^0.15.2",
35
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0",
36
+ "ts-node": "^10.9.2",
37
+ "uuid": "^9.0.1"
38
+ },
39
+ "devDependencies": {
40
+ "@types/jscodeshift": "^0.11.11",
41
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0",
42
+ "typescript": "^5.5.0"
43
+ },
44
+ "rsp": {
45
+ "type": "cli"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
50
+ "gitHead": "faf0e18467231422cb7a06eb92a74d04e271f1e8"
51
+ }