diff --git a/.changeset/cyan-gorillas-perform.md b/.changeset/cyan-gorillas-perform.md
new file mode 100644
index 000000000..0d4f52e93
--- /dev/null
+++ b/.changeset/cyan-gorillas-perform.md
@@ -0,0 +1,5 @@
+---
+'@nordcom/nordstar': patch
+---
+
+Improve internal tooling.
diff --git a/package.json b/package.json
index 17c403b50..e49fc172d 100644
--- a/package.json
+++ b/package.json
@@ -37,8 +37,8 @@
"clean:packages": "turbo clean --filter=!@nordcom/nordstar-docs --filter=!@nordcom/nordstar-storybook",
"clean:docs": "turbo clean --filter=@nordcom/nordstar-docs",
"clean:storybook": "turbo clean --filter=@nordcom/nordstar-storybook",
- "create:component": "plop component",
- "create:package": "plop package",
+ "create": "plop --plopfile ./plop/plopfile.js",
+ "create:component": "plop --plopfile ./plop/plopfile.js component",
"version": "changeset version",
"postversion": "npm install",
"version:unstable": "changeset version --snapshot unstable",
@@ -98,6 +98,7 @@
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-unused-imports": "3.0.0",
"husky": "8.0.3",
+ "inquirer-directory": "2.2.0",
"jsdom": "23.0.1",
"lint-staged": "15.2.0",
"plop": "4.0.0",
@@ -109,6 +110,7 @@
"react-dom": "18.2.0",
"rimraf": "5.0.5",
"sass": "1.69.5",
+ "strip-ansi": "7.1.0",
"turbo": "1.11.1",
"typescript": "5.3.3",
"vite": "5.0.6",
diff --git a/plop/component/src/{{componentName}}.test.tsx.hbs b/plop/component/src/{{componentName}}.test.tsx.hbs
index e1d3143f2..c3db6676b 100644
--- a/plop/component/src/{{componentName}}.test.tsx.hbs
+++ b/plop/component/src/{{componentName}}.test.tsx.hbs
@@ -1,3 +1,5 @@
+import '@testing-library/jest-dom';
+
import { describe, expect, it } from 'vitest';
import { {{capitalize componentName}} } from '../src';
diff --git a/plop/component/src/{{componentName}}.tsx.hbs b/plop/component/src/{{componentName}}.tsx.hbs
index 8e643b4e3..ba5f53405 100644
--- a/plop/component/src/{{componentName}}.tsx.hbs
+++ b/plop/component/src/{{componentName}}.tsx.hbs
@@ -6,13 +6,11 @@ export type {{capitalize componentName}}Props = {
as?: As;
};
-const {{capitalize componentName}} = forwardRef<'div', {{capitalize componentName}}Props>(
- ({ as: Tag = 'div', className, ...props }, ref) => {
- const classes = `${styles.container}${className ? ` ${className}` : ''}`;
+const {{capitalize componentName}} = forwardRef<'div', {{capitalize componentName}}Props>(({ as: Tag = 'div', className, ...props }, ref) => {
+ const classes = `${styles.container}${className ? ` ${className}` : ''}`;
- return ;
- }
-);
+ return ;
+});
{{capitalize componentName}}.displayName = 'Nordstar.{{capitalize componentName}}';
diff --git a/plop/component/tsconfig.json.hbs b/plop/component/tsconfig.json.hbs
index bf03994dc..3d29a2259 100644
--- a/plop/component/tsconfig.json.hbs
+++ b/plop/component/tsconfig.json.hbs
@@ -5,7 +5,7 @@
"rootDir": "./src",
"outDir": "dist"
},
- "exclude": ["node_modules", "dist", "src/**/*.test.*", "src/**/*.stories.*"],
- "extends": "../../../tsconfig.json",
- "include": ["src", "index.ts", "../../../@types/declaration.d.ts"]
+ "exclude": ["node_modules", "dist", "**/*.test.*", "**/*.stories.*"],
+ "extends": "../../tsconfig.json",
+ "include": ["./src/**/*.ts", "./**/*.tsx", "../../../@types/declaration.d.ts"]
}
diff --git a/plop/package/README.md.hbs b/plop/package/README.md.hbs
deleted file mode 100644
index 1b7417bbb..000000000
--- a/plop/package/README.md.hbs
+++ /dev/null
@@ -1,9 +0,0 @@
-# @nordcom/nordstar-{{packageName}}
-
-{{description}}
-
-## Installation
-
-```sh
-npm install @nordcom/nordstar-{{packageName}}
-```
diff --git a/plop/package/package.json.hbs b/plop/package/package.json.hbs
deleted file mode 100644
index d6338eff6..000000000
--- a/plop/package/package.json.hbs
+++ /dev/null
@@ -1,74 +0,0 @@
-{
- "$schema": "https://json.schemastore.org/package.json",
- "name": "@nordcom/nordstar-{{packageName}}",
- "type": "module",
- "version": "0.0.0",
- "description": "",
- "main": "./src/index.ts",
- "types": "./src/index.ts",
- "module": "./src/index.ts",
- "exports": {
- ".": {
- "import": "./src/index.ts",
- "types": "./src/index.ts"
- }
- },
- "sideEffects": false,
- "engines": {
- "npm": ">=8",
- "node": ">=18 <=21"
- },
- "scripts": {
- "build": "vite build",
- "dev": "vite build --watch",
- "clean": "rimraf dist .turbo",
- "typecheck": "tsc --noEmit",
- "prepack": "clean-package",
- "postpack": "clean-package restore"
- },
- "keywords": [
- "nordstar",
- "nordcom",
- "{{packageName}}"
- ],
- "author": {
- "name": "Nordcom Group Inc.",
- "email": "opensource@nordcom.io",
- "url": "https://nordcom.io/"
- },
- "contributors": [
- {
- "name": "Filiph Siitam Sandström",
- "email": "filiph@nordcom.io",
- "url": "https://github.com/filiphsps/"
- }
- ],
- "repository": {
- "type": "git",
- "url": "git+https://github.com/NordcomInc/nordstar.git",
- "directory": "packages/{{outDir}}/{{packageName}}"
- },
- "license": "MIT",
- "bugs": {
- "url": "https://github.com/NordcomInc/nordstar/issues"
- },
- "homepage": "https://nordstar.nordcom.io/",
- "files": [
- "dist"
- ],
- "publishConfig": {
- "access": "public"
- },
- "dependencies": {
- "@nordcom/nordstar-system": "0.0.0"
- },
- "peerDependencies": {
- "react": ">=18",
- "react-dom": ">=18"
- },
- "devDependencies": {
- "clean-package": "2.2.0",
- "react": "18.2.0"
- },
- "clean-package": "../../../clean-package.config.json"
-}
diff --git a/plop/package/src/index.ts.hbs b/plop/package/src/index.ts.hbs
deleted file mode 100644
index e69de29bb..000000000
diff --git a/plop/package/tsconfig.json.hbs b/plop/package/tsconfig.json.hbs
deleted file mode 100644
index bf03994dc..000000000
--- a/plop/package/tsconfig.json.hbs
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "$schema": "https://json.schemastore.org/tsconfig.json",
- "compilerOptions": {
- "baseUrl": "./",
- "rootDir": "./src",
- "outDir": "dist"
- },
- "exclude": ["node_modules", "dist", "src/**/*.test.*", "src/**/*.stories.*"],
- "extends": "../../../tsconfig.json",
- "include": ["src", "index.ts", "../../../@types/declaration.d.ts"]
-}
diff --git a/plop/package/vite.config.ts.hbs b/plop/package/vite.config.ts.hbs
deleted file mode 100644
index a05fa2f90..000000000
--- a/plop/package/vite.config.ts.hbs
+++ /dev/null
@@ -1,43 +0,0 @@
-import react from '@vitejs/plugin-react';
-import { resolve } from 'node:path';
-import { defineConfig } from 'vite';
-import dts from 'vite-plugin-dts';
-import { libInjectCss } from 'vite-plugin-lib-inject-css';
-import tsConfigPaths from 'vite-tsconfig-paths';
-
-export default defineConfig({
- root: resolve(__dirname),
- build: {
- target: 'esnext',
- copyPublicDir: false,
- emptyOutDir: true,
- outDir: resolve(__dirname, 'dist'),
- sourcemap: true,
- lib: {
- entry: ['src/index.ts', 'src/{{packageName}}.tsx'],
- formats: ['es']
- },
- rollupOptions: {
- external: ['react', 'react/jsx-runtime', 'react-dom', /^@nordcom\/nordstar-/],
- output: {
- globals: {
- react: 'React',
- 'react-dom': 'ReactDOM'
- },
- sourcemapExcludeSources: true
- }
- }
- },
- plugins: [
- react(),
- libInjectCss(),
- tsConfigPaths(),
- dts({
- clearPureImport: false,
- entryRoot: resolve(__dirname, 'src'),
- rollupTypes: false,
- insertTypesEntry: false,
- tsconfigPath: resolve(__dirname, 'tsconfig.json')
- })
- ]
-});
diff --git a/plop/page/{{lowerCase name}}/page.tsx.hbs b/plop/page/{{lowerCase name}}/page.tsx.hbs
new file mode 100644
index 000000000..617faef13
--- /dev/null
+++ b/plop/page/{{lowerCase name}}/page.tsx.hbs
@@ -0,0 +1,16 @@
+import { Heading, View } from '@nordcom/nordstar';
+import type { Metadata } from 'next';
+
+export type {{name}}PageProps = {};
+
+export const metadata: Metadata = {
+ title: '{{name}} Page'
+};
+
+export default async function {{name}}Page({}: {{name}}PageProps) {
+ return (
+
+ TODO
+
+ );
+}
diff --git a/plop/plopfile.js b/plop/plopfile.js
new file mode 100644
index 000000000..31e4a9dd5
--- /dev/null
+++ b/plop/plopfile.js
@@ -0,0 +1,313 @@
+import chalk from 'chalk';
+import DirectoryPrompt from 'inquirer-directory';
+import InputPrompt from 'inquirer/lib/prompts/input.js';
+import { mkdir } from 'node:fs';
+import path from 'node:path';
+import { moveCursor } from 'node:readline';
+import stripAnsi from 'strip-ansi';
+
+const themeColor = chalk.reset.hex('#ed1e79').bold;
+
+class NamingPrompt extends InputPrompt {
+ #prevRaw;
+
+ _run(cb) {
+ this.#prevRaw = process.stdin.isRaw;
+ if (this.opt.transformer) {
+ process.stdin.setRawMode(true);
+ }
+
+ super._run(cb);
+ }
+
+ render(error) {
+ error = error ? chalk.red(error) : '';
+
+ const transformer = this.opt.transformer;
+ const question = this.getQuestion();
+
+ const final = !error && ['answered', 'done'].includes(this.status);
+
+ if (!transformer) {
+ this.screen.render(`${question}${this.rl.line}`, error);
+ return;
+ }
+
+ const transformed = stripAnsi(transformer(this.rl.line, this.answers, { isFinal: final }));
+ const suffix = transformed.replaceAll(this.rl.line, '').trim();
+ const content = transformed.slice(0, -suffix.length);
+
+ let body = `${!final ? content : this.answer}${chalk.dim(suffix)}`;
+ if (final) body = chalk.cyan(stripAnsi(body));
+ this.screen.render(`${question}${body}`, error);
+
+ if (!final) {
+ moveCursor(process.stdout, -suffix.length, 0);
+ return;
+ }
+ }
+
+ onEnd(state) {
+ this.answer = state.value;
+ this.status = 'answered';
+
+ // Re-render prompt
+ this.render();
+
+ this.screen.done();
+ this.done(state.value);
+
+ // Restore raw mode.
+ if (this.opt.transformer) {
+ process.stdin.setRawMode(this.#prevRaw);
+ }
+ }
+
+ onError(state) {
+ this.render(state.isValid);
+ }
+}
+
+const capitalize = (str) => {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+};
+
+const camelCase = (str) => {
+ return str.replace(/[-_](\w)/g, (_, c) => c.toUpperCase());
+};
+
+/**
+ * @param {import("plop").NodePlopAPI} plop
+ */
+export default function main(plop) {
+ plop.setWelcomeMessage(`${chalk.reset(themeColor('What are we generating today?'))}`);
+
+ plop.setHelper('capitalize', (text) => {
+ return capitalize(camelCase(text));
+ });
+ plop.setHelper('camelCase', (text) => {
+ return camelCase(text);
+ });
+ plop.setHelper('lowerCase', (text) => {
+ return text.toLowerCase();
+ });
+
+ plop.setPrompt('directory', DirectoryPrompt);
+ plop.setPrompt('naming', NamingPrompt);
+
+ ['component'].forEach((gen) => {
+ plop.setGenerator(gen, {
+ description: `Generate a new ${gen}.`,
+ prompts: [
+ {
+ type: 'input',
+ name: `${gen}Name`,
+ message: `Enter ${gen} name:`,
+
+ validate: (value) => {
+ if (!value) {
+ return `${gen} name is required.`;
+ }
+
+ // check is case is correct
+ if (value !== value.toLowerCase()) {
+ return `${gen} name must be in lowercase.`;
+ }
+
+ // cannot have spaces
+ if (value.includes(' ')) {
+ return `${gen} name cannot have spaces.`;
+ }
+
+ return true;
+ }
+ },
+ {
+ type: 'input',
+ name: 'description',
+ message: `The description of this ${gen}:`,
+
+ validate: (value) => {
+ if (!value) {
+ return `${gen} description is required.`;
+ }
+
+ if (!value.endsWith('.')) {
+ return `${gen} description must end with a period.`;
+ }
+
+ return true;
+ }
+ },
+ {
+ type: 'directory',
+ name: 'outDir',
+ message: `Where should this ${gen} live?`,
+ basePath: './packages',
+ default: './packages/components',
+
+ validate: (value) => {
+ if (!value) {
+ return `outDir is required`;
+ }
+
+ return true;
+ }
+ }
+ ],
+ actions(answers) {
+ const actions = [];
+
+ if (!answers) return actions;
+
+ const { description, outDir } = answers;
+ const generatorName = answers[`${gen}Name`] ?? '';
+
+ const data = {
+ [`${gen}Name`]: generatorName,
+ description,
+ outDir
+ };
+
+ actions.push({
+ type: 'addMany',
+ templateFiles: `${gen}/**`,
+ destination: `../packages/{{outDir}}/{{dashCase ${gen}Name}}`,
+ base: `${gen}`,
+ data,
+ abortOnFail: true
+ });
+
+ return actions;
+ }
+ });
+ });
+
+ plop.setGenerator('page', {
+ description: `(TODO) Create a page for the docs app.`,
+ prompts: async ({ prompt }) => {
+ return prompt([
+ {
+ type: 'naming',
+ name: `name`,
+ message: `Enter the page name:`,
+
+ transformer(value) {
+ if (value.length <= 0) return value;
+
+ return `${value}Page`;
+ },
+
+ validate(value) {
+ if (!value) {
+ return `The name of the page is required.`;
+ }
+
+ if (value.endsWith('Page')) {
+ return `The name should not end with "Page".`;
+ }
+
+ // check is case is correct
+ if (value.at(0) !== value.at(0).toUpperCase()) {
+ return `The name of the page must be capitalized.`;
+ }
+
+ // cannot have spaces
+ if (value.includes(' ')) {
+ return `The name of the page cannot have spaces.`;
+ }
+
+ return true;
+ }
+ },
+ {
+ type: 'list',
+ name: 'create_dir',
+ message: `Create a new subdirectory?`,
+ default: 'Use existing',
+ choices: ['Use existing', 'Create new'],
+
+ validate(value) {
+ if (!value) {
+ return `create_dir is required`;
+ }
+
+ return true;
+ }
+ },
+ {
+ type: 'directory',
+ name: 'create_dir_root',
+ message: `Where should we create the subdirectory?`,
+ basePath: './docs/src/app',
+ async when(answers) {
+ return answers.create_dir === 'Create new';
+ }
+ },
+ {
+ type: 'input',
+ name: `create_dir_name`,
+ message: `Enter the name of the subdirectory:`,
+ async when(answers) {
+ return answers.create_dir === 'Create new';
+ },
+
+ transformer(value) {
+ return value;
+ },
+
+ filter(value) {
+ return new Promise((resolve, reject) => {
+ try {
+ mkdir(path.join('./docs/src/app/', value), { recursive: true }, () => resolve(value));
+ } catch (error) {
+ reject(error);
+ }
+ });
+ },
+
+ validate(value) {
+ if (!value) {
+ return `The name of the page is required.`;
+ }
+
+ return true;
+ }
+ },
+ {
+ type: 'directory',
+ name: 'path',
+ message: `Where should we create the page`,
+ basePath: './docs/src/app',
+
+ validate: (value) => {
+ if (!value) {
+ return `path is required`;
+ }
+
+ return true;
+ }
+ }
+ ]);
+ },
+
+ actions(answers) {
+ if (!ansers) return [];
+
+ const { name, path } = answers;
+ return [
+ {
+ type: 'addMany',
+ templateFiles: `page/**`,
+ destination: `../docs/src/app/{{path}}`,
+ base: `page`,
+ data: {
+ name,
+ path
+ },
+ abortOnFail: true
+ }
+ ];
+ }
+ });
+}
diff --git a/plopfile.cjs b/plopfile.cjs
deleted file mode 100644
index b21085324..000000000
--- a/plopfile.cjs
+++ /dev/null
@@ -1,111 +0,0 @@
-const capitalize = (str) => {
- return str.charAt(0).toUpperCase() + str.slice(1);
-};
-
-const camelCase = (str) => {
- return str.replace(/[-_](\w)/g, (_, c) => c.toUpperCase());
-};
-
-const workspaces = ['components', 'core'];
-const generators = ['component', 'package'];
-
-const defaultOutDirs = {
- component: 'components',
- package: 'core'
-};
-
-/**
- * @param {import("plop").NodePlopAPI} plop
- */
-module.exports = function main(plop) {
- plop.setHelper('capitalize', (text) => {
- return capitalize(camelCase(text));
- });
- plop.setHelper('camelCase', (text) => {
- return camelCase(text);
- });
-
- generators.forEach((gen) => {
- plop.setGenerator(gen, {
- description: `Generates a ${gen}`,
- prompts: [
- {
- type: 'input',
- name: `${gen}Name`,
- message: `Enter ${gen} name:`,
-
- validate: (value) => {
- if (!value) {
- return `${gen} name is required`;
- }
-
- // check is case is correct
- if (value !== value.toLowerCase()) {
- return `${gen} name must be in lowercase`;
- }
-
- // cannot have spaces
- if (value.includes(' ')) {
- return `${gen} name cannot have spaces`;
- }
-
- return true;
- }
- },
- {
- type: 'input',
- name: 'description',
- message: `The description of this ${gen}:`,
-
- validate: (value) => {
- if (!value) {
- return `${gen} description is required`;
- }
-
- return true;
- }
- },
- {
- type: 'list',
- name: 'outDir',
- message: `Where should this ${gen} live?`,
- default: defaultOutDirs[gen],
- choices: workspaces,
-
- validate: (value) => {
- if (!value) {
- return `outDir is required`;
- }
-
- return true;
- }
- }
- ],
- actions(answers) {
- const actions = [];
-
- if (!answers) return actions;
-
- const { description, outDir } = answers;
- const generatorName = answers[`${gen}Name`] ?? '';
-
- const data = {
- [`${gen}Name`]: generatorName,
- description,
- outDir
- };
-
- actions.push({
- type: 'addMany',
- templateFiles: `plop/${gen}/**`,
- destination: `./packages/{{outDir}}/{{dashCase ${gen}Name}}`,
- base: `plop/${gen}`,
- data,
- abortOnFail: true
- });
-
- return actions;
- }
- });
- });
-};
diff --git a/tsconfig.test.json b/tsconfig.test.json
index f770518b0..9b0066794 100644
--- a/tsconfig.test.json
+++ b/tsconfig.test.json
@@ -6,7 +6,7 @@
"types": ["vitest/globals", "@testing-library/jest-dom"]
},
- "include": ["packages", "@types/declaration.d.ts", "**/*.test.*"],
+ "include": ["packages", "@types/declaration.d.ts", "**/*.test.*", "plop/**/*.js"],
"exclude": [
"**/node_modules",
"**/dist",