@codemod-utils/blueprints
Advanced tools
Comparing version 1.0.0 to 1.1.0
type PackageName = string; | ||
type PackageVersion = string; | ||
type Options = { | ||
/** | ||
* Returns the version that can be installed for a package. | ||
* | ||
* Always favors the current version in the user's project (a no-op). | ||
* Uses the latest version only if the project doesn't depend on the | ||
* package yet. | ||
* | ||
* @param packageName | ||
* | ||
* Name of the package. | ||
* | ||
* @param options | ||
* | ||
* An object with `dependencies` (the current versions in the user's | ||
* project) and `latestVersions` (the versions to install by default). | ||
* | ||
* @return | ||
* | ||
* The version to install. | ||
* | ||
* @example | ||
* | ||
* First, pass `latestVersions` to `decideVersion()`. | ||
* | ||
* ```ts | ||
* const latestVersions = new Map([ | ||
* ['embroider-css-modules', '1.0.0'], | ||
* ['webpack', '5.89.0'], | ||
* ]); | ||
* | ||
* // Create a wrapper | ||
* function getVersion(packageName, options) { | ||
* const { dependencies } = options; | ||
* | ||
* return decideVersion(packageName, { | ||
* dependencies, | ||
* latestVersions, | ||
* }); | ||
* } | ||
* ``` | ||
* | ||
* Then, pass `dependencies` to `decideVersion()`. | ||
* | ||
* ```ts | ||
* const options = { | ||
* dependencies: new Map([ | ||
* ['webpack', '^5.82.0'], | ||
* ]), | ||
* }; | ||
* | ||
* getVersion('embroider-css-modules', options); // '^1.0.0' | ||
* getVersion('webpack', options); // '^5.82.0' (no-op) | ||
* ``` | ||
*/ | ||
export declare function decideVersion(packageName: PackageName, options: { | ||
dependencies: Map<PackageName, PackageVersion>; | ||
latestVersions: Map<PackageName, PackageVersion>; | ||
}; | ||
export declare function decideVersion(packageName: PackageName, options: Options): PackageVersion; | ||
}): PackageVersion; | ||
export {}; |
@@ -0,1 +1,55 @@ | ||
/** | ||
* Returns the version that can be installed for a package. | ||
* | ||
* Always favors the current version in the user's project (a no-op). | ||
* Uses the latest version only if the project doesn't depend on the | ||
* package yet. | ||
* | ||
* @param packageName | ||
* | ||
* Name of the package. | ||
* | ||
* @param options | ||
* | ||
* An object with `dependencies` (the current versions in the user's | ||
* project) and `latestVersions` (the versions to install by default). | ||
* | ||
* @return | ||
* | ||
* The version to install. | ||
* | ||
* @example | ||
* | ||
* First, pass `latestVersions` to `decideVersion()`. | ||
* | ||
* ```ts | ||
* const latestVersions = new Map([ | ||
* ['embroider-css-modules', '1.0.0'], | ||
* ['webpack', '5.89.0'], | ||
* ]); | ||
* | ||
* // Create a wrapper | ||
* function getVersion(packageName, options) { | ||
* const { dependencies } = options; | ||
* | ||
* return decideVersion(packageName, { | ||
* dependencies, | ||
* latestVersions, | ||
* }); | ||
* } | ||
* ``` | ||
* | ||
* Then, pass `dependencies` to `decideVersion()`. | ||
* | ||
* ```ts | ||
* const options = { | ||
* dependencies: new Map([ | ||
* ['webpack', '^5.82.0'], | ||
* ]), | ||
* }; | ||
* | ||
* getVersion('embroider-css-modules', options); // '^1.0.0' | ||
* getVersion('webpack', options); // '^5.82.0' (no-op) | ||
* ``` | ||
*/ | ||
export function decideVersion(packageName, options) { | ||
@@ -2,0 +56,0 @@ const { dependencies, latestVersions } = options; |
@@ -0,1 +1,43 @@ | ||
/** | ||
* Returns where `npx` installs the codemod on the user's machine. | ||
* | ||
* @param fileURL | ||
* | ||
* Pass the value of `import.meta.url`. | ||
* | ||
* @return | ||
* | ||
* The installation path. | ||
* | ||
* @example | ||
* | ||
* To read blueprint files, get the path to the `blueprints` folder. | ||
* | ||
* ```ts | ||
* // src/utils/blueprints/blueprints-root.ts | ||
* import { join } from 'node:path'; | ||
* | ||
* const fileURL = import.meta.url; | ||
* | ||
* const blueprintsRoot = join(getFilePath(fileURL), '../../blueprints'); | ||
* | ||
* // '<some/absolute/path>/src/blueprints' | ||
* ``` | ||
* | ||
* Afterwards, prepend the file path with `blueprintsRoot`. | ||
* | ||
* ```ts | ||
* import { readFileSync } from 'node:fs'; | ||
* import { join } from 'node:path'; | ||
* | ||
* const blueprintFilePaths = ['LICENSE.md', 'README.md']; | ||
* | ||
* blueprintFilePaths.forEach((blueprintFilePath) => { | ||
* const blueprintFile = readFileSync( | ||
* join(blueprintsRoot, blueprintFilePath), | ||
* 'utf8', | ||
* ); | ||
* }); | ||
* ``` | ||
*/ | ||
export declare function getFilePath(fileURL: string): string; |
import { dirname } from 'node:path'; | ||
import { fileURLToPath } from 'node:url'; | ||
/** | ||
* Returns where `npx` installs the codemod on the user's machine. | ||
* | ||
* @param fileURL | ||
* | ||
* Pass the value of `import.meta.url`. | ||
* | ||
* @return | ||
* | ||
* The installation path. | ||
* | ||
* @example | ||
* | ||
* To read blueprint files, get the path to the `blueprints` folder. | ||
* | ||
* ```ts | ||
* // src/utils/blueprints/blueprints-root.ts | ||
* import { join } from 'node:path'; | ||
* | ||
* const fileURL = import.meta.url; | ||
* | ||
* const blueprintsRoot = join(getFilePath(fileURL), '../../blueprints'); | ||
* | ||
* // '<some/absolute/path>/src/blueprints' | ||
* ``` | ||
* | ||
* Afterwards, prepend the file path with `blueprintsRoot`. | ||
* | ||
* ```ts | ||
* import { readFileSync } from 'node:fs'; | ||
* import { join } from 'node:path'; | ||
* | ||
* const blueprintFilePaths = ['LICENSE.md', 'README.md']; | ||
* | ||
* blueprintFilePaths.forEach((blueprintFilePath) => { | ||
* const blueprintFile = readFileSync( | ||
* join(blueprintsRoot, blueprintFilePath), | ||
* 'utf8', | ||
* ); | ||
* }); | ||
* ``` | ||
*/ | ||
export function getFilePath(fileURL) { | ||
@@ -4,0 +46,0 @@ const __filename = fileURLToPath(fileURL); |
@@ -0,1 +1,73 @@ | ||
/** | ||
* Returns the blueprint file after filling it out with data. | ||
* | ||
* @param file | ||
* | ||
* A blueprint file, which may contain escape, evaluate, and | ||
* interpolate delimiters. | ||
* | ||
* - Escape (`<%- %>`) - escape an HTML code | ||
* - Evaluate (`<% %>`) - evaluate a JavaScript code | ||
* - Interpolate (`<%= %>`) - substitute a value | ||
* | ||
* @param data | ||
* | ||
* An object that provides the data needed for the file. | ||
* | ||
* @return | ||
* | ||
* The processed blueprint file. | ||
* | ||
* @example | ||
* | ||
* First, create a blueprint file. | ||
* | ||
* ```ts | ||
* // blueprints/__testAppLocation__/ember-cli-build.js | ||
* 'use strict'; | ||
* | ||
* const EmberApp = require('ember-cli/lib/broccoli/ember-app'); | ||
* | ||
* module.exports = function (defaults) { | ||
* const app = new EmberApp(defaults, { | ||
* // Add options here | ||
* autoImport: { | ||
* watchDependencies: ['<%= addon.name %>'], | ||
* },<% if (testApp.hasTypeScript) { %> | ||
* 'ember-cli-babel': { | ||
* enableTypeScriptTransform: true, | ||
* },<% } %> | ||
* }); | ||
* | ||
* const { maybeEmbroider } = require('@embroider/test-setup'); | ||
* | ||
* return maybeEmbroider(app); | ||
* }; | ||
* ``` | ||
* | ||
* Then, pass data to the file. | ||
* | ||
* ```ts | ||
* import { readFileSync } from 'node:fs'; | ||
* import { join } from 'node:path'; | ||
* | ||
* // Read file | ||
* const blueprintFilePath = '__testAppLocation__/ember-cli-build.js'; | ||
* | ||
* const blueprintFile = readFileSync( | ||
* join(blueprintsRoot, blueprintFilePath), | ||
* 'utf8', | ||
* ); | ||
* | ||
* // Process file | ||
* processTemplate(blueprintFile, { | ||
* addon: { | ||
* name: 'ember-container-query', | ||
* }, | ||
* app: { | ||
* hasTypeScript: true, | ||
* }, | ||
* }); | ||
* ``` | ||
*/ | ||
export declare function processTemplate(file: string, data?: object): string; |
import template from 'lodash.template'; | ||
/** | ||
* Returns the blueprint file after filling it out with data. | ||
* | ||
* @param file | ||
* | ||
* A blueprint file, which may contain escape, evaluate, and | ||
* interpolate delimiters. | ||
* | ||
* - Escape (`<%- %>`) - escape an HTML code | ||
* - Evaluate (`<% %>`) - evaluate a JavaScript code | ||
* - Interpolate (`<%= %>`) - substitute a value | ||
* | ||
* @param data | ||
* | ||
* An object that provides the data needed for the file. | ||
* | ||
* @return | ||
* | ||
* The processed blueprint file. | ||
* | ||
* @example | ||
* | ||
* First, create a blueprint file. | ||
* | ||
* ```ts | ||
* // blueprints/__testAppLocation__/ember-cli-build.js | ||
* 'use strict'; | ||
* | ||
* const EmberApp = require('ember-cli/lib/broccoli/ember-app'); | ||
* | ||
* module.exports = function (defaults) { | ||
* const app = new EmberApp(defaults, { | ||
* // Add options here | ||
* autoImport: { | ||
* watchDependencies: ['<%= addon.name %>'], | ||
* },<% if (testApp.hasTypeScript) { %> | ||
* 'ember-cli-babel': { | ||
* enableTypeScriptTransform: true, | ||
* },<% } %> | ||
* }); | ||
* | ||
* const { maybeEmbroider } = require('@embroider/test-setup'); | ||
* | ||
* return maybeEmbroider(app); | ||
* }; | ||
* ``` | ||
* | ||
* Then, pass data to the file. | ||
* | ||
* ```ts | ||
* import { readFileSync } from 'node:fs'; | ||
* import { join } from 'node:path'; | ||
* | ||
* // Read file | ||
* const blueprintFilePath = '__testAppLocation__/ember-cli-build.js'; | ||
* | ||
* const blueprintFile = readFileSync( | ||
* join(blueprintsRoot, blueprintFilePath), | ||
* 'utf8', | ||
* ); | ||
* | ||
* // Process file | ||
* processTemplate(blueprintFile, { | ||
* addon: { | ||
* name: 'ember-container-query', | ||
* }, | ||
* app: { | ||
* hasTypeScript: true, | ||
* }, | ||
* }); | ||
* ``` | ||
*/ | ||
export function processTemplate(file, data) { | ||
@@ -3,0 +75,0 @@ const settings = { |
{ | ||
"name": "@codemod-utils/blueprints", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "Utilities for blueprints", | ||
@@ -43,12 +43,12 @@ "keywords": [ | ||
"@sondr3/minitest": "^0.1.2", | ||
"@types/lodash.template": "^4.5.1", | ||
"@types/node": "^18.17.15", | ||
"concurrently": "^8.2.1", | ||
"eslint": "^8.49.0", | ||
"@types/lodash.template": "^4.5.2", | ||
"@types/node": "^18.18.7", | ||
"concurrently": "^8.2.2", | ||
"eslint": "^8.52.0", | ||
"prettier": "^3.0.3", | ||
"typescript": "^5.2.2", | ||
"@codemod-utils/tests": "1.0.0", | ||
"@shared-configs/typescript": "0.0.0", | ||
"@shared-configs/prettier": "0.0.0", | ||
"@shared-configs/typescript": "0.0.0", | ||
"@shared-configs/eslint-config-node": "0.0.0" | ||
"@shared-configs/eslint-config-node": "0.0.0", | ||
"@codemod-utils/tests": "1.1.1" | ||
}, | ||
@@ -55,0 +55,0 @@ "engines": { |
116
README.md
@@ -17,8 +17,11 @@ [![This project uses GitHub Actions for continuous integration.](https://github.com/ijlee2/codemod-utils/actions/workflows/ci.yml/badge.svg)](https://github.com/ijlee2/codemod-utils/actions/workflows/ci.yml) | ||
Need to add or update a dependency? You can use `decideVersion` to know which version to install. | ||
Returns the version that can be installed for a package. | ||
It is assumed that: | ||
Always favors the current version in the user's project (a no-op). Uses the latest version only if the project doesn't depend on the package yet. | ||
- You don't want to rely on a library such as [`latest-version`](https://www.npmjs.com/package/latest-version). (The reasons are, your codemod would have an extra dependency and your tests may fail without stubs—more dependencies.) | ||
- Before calling `decideVersion`, the codemod has computed `dependencies` (current dependencies of the user's project) and stored `latestVersions` (versions to install by default) somewhere. | ||
> [!NOTE] | ||
> It is assumed that: | ||
> | ||
> - You don't want to rely on a library such as [`latest-version`](https://www.npmjs.com/package/latest-version). (The reasons are, your codemod would have an extra dependency and your tests may fail without stubs—more dependencies.) | ||
> - Before calling `decideVersion`, the codemod has computed `dependencies` (current dependencies of the user's project) and stored `latestVersions` (versions to install by default) somewhere. | ||
@@ -29,15 +32,14 @@ <details> | ||
Step 1. Pass `latestVersions` to `decideVersion`. | ||
First, pass `latestVersions` to `decideVersion()`. | ||
```js | ||
```ts | ||
import { decideVersion } from '@codemod-utils/blueprints'; | ||
// Hardcode the versions | ||
const latestVersions = new Map([ | ||
['embroider-css-modules', '0.1.2'], | ||
['webpack', '5.82.0'], | ||
['embroider-css-modules', '1.0.0'], | ||
['webpack', '5.89.0'], | ||
]); | ||
// Create a wrapper | ||
export function getVersion(packageName, options) { | ||
function getVersion(packageName, options) { | ||
const { dependencies } = options; | ||
@@ -52,15 +54,13 @@ | ||
Step 2. Pass `dependencies` to `decideVersion`. | ||
Then, pass `dependencies` to `decideVersion()`. | ||
```js | ||
// `dependencies` obtained from the user's `package.json` | ||
```ts | ||
const options = { | ||
dependencies: new Map([ | ||
['webpack', '^5.79.0'], | ||
['webpack', '^5.82.0'], | ||
]), | ||
}; | ||
// Query version | ||
getVersion('embroider-css-modules', options); // '^0.1.2' | ||
getVersion('webpack', options); // '^5.79.0' (no-op) | ||
getVersion('embroider-css-modules', options); // '^1.0.0' | ||
getVersion('webpack', options); // '^5.82.0' (no-op) | ||
``` | ||
@@ -73,3 +73,3 @@ | ||
When a user runs your codemod with `npx`, where are your blueprint files installed? With `getFilePath`, you don't need to worry about the actual location. | ||
Returns where `npx` installs the codemod on the user's machine. | ||
@@ -80,5 +80,6 @@ <details> | ||
Step 1. Pass `import.meta.url` to `getFilePath`. Append the relative path to your blueprints folder. | ||
To read blueprint files, get the path to the `blueprints` folder. | ||
```js | ||
```ts | ||
/* src/utils/blueprints/blueprints-root.ts */ | ||
import { join } from 'node:path'; | ||
@@ -90,19 +91,21 @@ | ||
// Create a wrapper | ||
export const blueprintsRoot = join(getFilePath(fileURL), '../../blueprints'); | ||
const blueprintsRoot = join(getFilePath(fileURL), '../../blueprints'); | ||
// '<some/absolute/path>/src/blueprints' | ||
``` | ||
Step 2. Prepend the file path with `blueprintsRoot`. | ||
Afterwards, prepend the file path with `blueprintsRoot`. | ||
```js | ||
```ts | ||
import { readFileSync } from 'node:fs'; | ||
import { join } from 'node:path'; | ||
// Read file | ||
const blueprintFilePath = '__addonLocation__/rollup.config.mjs'; | ||
const blueprintFilePaths = ['LICENSE.md', 'README.md']; | ||
const blueprintFile = readFileSync( | ||
join(blueprintsRoot, blueprintFilePath), | ||
'utf8', | ||
); | ||
blueprintFilePaths.forEach((blueprintFilePath) => { | ||
const blueprintFile = readFileSync( | ||
join(blueprintsRoot, blueprintFilePath), | ||
'utf8', | ||
); | ||
}); | ||
``` | ||
@@ -115,3 +118,3 @@ | ||
Often, blueprints need context: When a condition is true, a file should be generated in a different way. You can [embed logic with delimiters](https://lodash.com/docs/#template) in the blueprint files, then use `processTemplate` to pass data. | ||
Often, blueprints need context: If some condition is true, a file should be generated in a different way. You can [embed logic with delimiters](https://lodash.com/docs/#template) in the blueprint files, then use `processTemplate` to pass data. | ||
@@ -128,33 +131,30 @@ There are 3 types of delimiters: | ||
Step 1. Indicate how the file should be created. | ||
First, create a blueprint file. | ||
```js | ||
/* blueprints/__addonLocation__/rollup.config.mjs */ | ||
<% if (options.packages.addon.hasTypeScript) { %>import typescript from 'rollup-plugin-ts';<% } else { %>import { babel } from '@rollup/plugin-babel';<% } %> | ||
import copy from 'rollup-plugin-copy'; | ||
import { Addon } from '@embroider/addon-dev/rollup'; | ||
```ts | ||
/* blueprints/__testAppLocation__/ember-cli-build.js */ | ||
'use strict'; | ||
const addon = new Addon({ | ||
srcDir: 'src', | ||
destDir: 'dist', | ||
}); | ||
const EmberApp = require('ember-cli/lib/broccoli/ember-app'); | ||
export default { | ||
output: addon.output(), | ||
module.exports = function (defaults) { | ||
const app = new EmberApp(defaults, { | ||
// Add options here | ||
autoImport: { | ||
watchDependencies: ['<%= addon.name %>'], | ||
},<% if (testApp.hasTypeScript) { %> | ||
'ember-cli-babel': { | ||
enableTypeScriptTransform: true, | ||
},<% } %> | ||
}); | ||
plugins: [ | ||
addon.publicEntrypoints([<%= context.addon.publicEntrypoints.map((filePath) => `'${filePath}'`).join(', ') %>]), | ||
const { maybeEmbroider } = require('@embroider/test-setup'); | ||
addon.appReexports([<%= context.addon.appReexports.map((filePath) => `'${filePath}'`).join(', ') %>]), | ||
addon.dependencies(), | ||
// ... | ||
], | ||
return maybeEmbroider(app); | ||
}; | ||
``` | ||
Step 2. Pass data to the file. | ||
Then, pass data to the file. | ||
```js | ||
```ts | ||
import { readFileSync } from 'node:fs'; | ||
@@ -166,3 +166,3 @@ import { join } from 'node:path'; | ||
// Read file | ||
const blueprintFilePath = '__addonLocation__/rollup.config.mjs'; | ||
const blueprintFilePath = '__testAppLocation__/ember-cli-build.js'; | ||
@@ -176,4 +176,8 @@ const blueprintFile = readFileSync( | ||
processTemplate(blueprintFile, { | ||
context, // context = { addon: ... } | ||
options, // options = { packages: ... } | ||
addon: { | ||
name: 'ember-container-query', | ||
}, | ||
app: { | ||
hasTypeScript: true, | ||
}, | ||
}); | ||
@@ -180,0 +184,0 @@ ``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
16501
379
194
1