@coveo/create-atomic-component
Version:
Initialize a Coveo Atomic Component
171 lines (152 loc) • 4.65 kB
JavaScript
import '@coveo/create-atomic-component-project';
import {
cpSync,
readFileSync,
renameSync,
unlinkSync,
writeFileSync,
} from 'node:fs';
import {dirname, resolve} from 'node:path';
import {cwd} from 'node:process';
import {fileURLToPath} from 'node:url';
/***************** TODO: CDX-1428: Move to @coveo/create-atomic-commons package ******************/
class SerializableAggregateError extends AggregateError {
toJSON() {
return {
name: this.name,
message: this.message,
stack: this.stack,
options: this.options,
errors: Array.from(this.errors),
};
}
}
const handleErrors = (error) => {
if (process.channel) {
process.send(error);
process.exit(1);
} else {
throw error;
}
};
const successMessage = () => {
console.log(`
Project successfully configured
We suggest that you begin by typing:
$ npm install
$ npm start
$ npm start
Starts the development server.
$ npm run build
Builds your project in production mode.
Happy coding!
Further reading:
https://docs.coveo.com/en/atomic/latest/cc-search/create-custom-components
`);
};
const camelize = (str) =>
str
.replace(/-(.)/g, (_, group) => group.toUpperCase())
.replace(/^./, (match) => match.toUpperCase());
const transform = (transformers) => {
for (const {srcPath, destPath, transform} of transformers) {
if (!srcPath) {
continue;
}
if (!destPath) {
unlinkSync(srcPath);
continue;
}
renameSync(srcPath, destPath);
if (transform) {
writeFileSync(destPath, transform(readFileSync(destPath, 'utf8')));
}
}
};
// Adapted from Stencil: https://github.com/ionic-team/stencil/blob/main/src/utils/validation.ts
/**
* Validates that a component tag meets required naming conventions to be used for a web component
* @param tag the tag to validate
* @returns an error message if the tag has an invalid name, undefined if the tag name passes all checks
*/
const ensureComponentValidity = (tag) => {
const errors = [];
const alphaAndHyphenOnly = /^[a-z-]+$/;
const forbiddenLeadingHyphen = /^-/;
const forbiddenTrailingHyphen = /-$/;
const forbiddenMultiHyphen = /-{2,}/;
const shouldContainAtLeastOneHyphen = /-/;
if (!alphaAndHyphenOnly.test(tag)) {
errors.push(`"${tag}" can only contain lower case alphabetical characters`);
}
if (forbiddenLeadingHyphen.test(tag)) {
errors.push(`"${tag}" cannot start with a dash (-)`);
}
if (forbiddenTrailingHyphen.test(tag)) {
errors.push(`"${tag}" cannot end with a dash (-)`);
}
if (!shouldContainAtLeastOneHyphen.test(tag)) {
errors.push(
`"${tag}" must contain a dash (-) to work as a valid web component`
);
}
if (forbiddenMultiHyphen.test(tag)) {
errors.push(
`"${tag}" cannot contain multiple dashes (--) next to each other`
);
}
if (errors.length > 0) {
handleErrors(
new SerializableAggregateError(errors, 'Invalid component tag name')
);
}
};
/***********************************/
const __dirname = dirname(fileURLToPath(import.meta.url));
const templateRelativeDir = 'template';
const templateDirPath = resolve(__dirname, templateRelativeDir);
cpSync(templateDirPath, cwd(), {
recursive: true,
});
const componentName = process.argv[2];
if (componentName) {
ensureComponentValidity(componentName);
const transformers = [
{
srcPath: 'src/components/sample-component',
destPath: `src/components/${componentName}`,
},
{
srcPath: `src/components/${componentName}/src/sample-component.tsx`,
destPath: `src/components/${componentName}/src/${componentName}.tsx`,
transform: (text) =>
text
.replaceAll(/sample-component/g, componentName)
.replaceAll(/SampleComponent/g, camelize(componentName)),
},
{
srcPath: `src/components/${componentName}/src/sample-component.css`,
destPath: `src/components/${componentName}/src/${componentName}.css`,
},
{
srcPath: `src/components/${componentName}/package.json`,
destPath: `src/components/${componentName}/package.json`,
transform: (text) =>
text.replaceAll(/(@coveo\/)?sample-component/g, componentName),
},
{
srcPath: 'package.json',
destPath: 'package.json',
transform: (content) => {
const packageJson = JSON.parse(content);
for (const script of packageJson.scripts) {
script.name = script.name.substring(1);
}
return JSON.stringify(packageJson, null, 2);
},
},
];
transform(transformers);
}
successMessage();