@codemod-utils/blueprints
v1.1.5
Published
Utilities for blueprints
Downloads
65
Readme
@codemod-utils/blueprints
Utilities for blueprints
What is it?
Are there files that you always need, in order to create or update projects? @codemod-utils/blueprints
helps you define these files and provide user-specific data (context).
API
decideVersion
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.
[!NOTE] It is assumed that:
- You don't want to rely on a library such as
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 computeddependencies
(current dependencies of the user's project) and storedlatestVersions
(versions to install by default) somewhere.
First, pass latestVersions
to decideVersion()
.
import { decideVersion } from '@codemod-utils/blueprints';
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()
.
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)
getFilePath
Returns where npx
installs the codemod on the user's machine.
To read blueprint files, get the path to the blueprints
folder.
/* src/utils/blueprints/blueprints-root.ts */
import { join } from 'node:path';
import { getFilePath } from '@codemod-utils/blueprints';
const fileURL = import.meta.url;
const blueprintsRoot = join(getFilePath(fileURL), '../../blueprints');
// '<some/absolute/path>/src/blueprints'
Afterwards, prepend the file path with blueprintsRoot
.
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',
);
});
processTemplate
Often, blueprints need context: If some condition is true, a file should be generated in a different way. You can embed logic with delimiters in the blueprint files, then use processTemplate
to pass data.
There are 3 types of delimiters:
- Escape (
<%- %>
) - escape an HTML code - Evaluate (
<% %>
) - evaluate a JavaScript code - Interpolate (
<%= %>
) - substitute a value
First, create a blueprint file.
/* 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.
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { processTemplate } from '@codemod-utils/blueprints';
// 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,
},
});
Often, it is easy to miscalculate data and misplace the newline character \n
. If you intend to publish your codemod, I recommend using @codemod-utils/tests
(create and test file fixtures) to check the output and prevent regressions.
Compatibility
- Node.js v18 or above
Contributing
See the Contributing guide for details.
License
This project is licensed under the MIT License.