diff --git a/packages/utils/cli/bin/core/Logger.mjs b/packages/utils/cli/bin/core/Logger.mjs new file mode 100644 index 000000000..4b13436eb --- /dev/null +++ b/packages/utils/cli/bin/core/Logger.mjs @@ -0,0 +1,19 @@ +import chalk from 'chalk' + +export class Logger { + success(message) { + console.log(chalk.green(message)) + } + + error(message) { + console.log(chalk.red(message)) + } + + info(message) { + console.log(chalk.yellow(message)) + } + + warning(message) { + console.log(chalk.green(message)) + } +} diff --git a/packages/utils/cli/bin/core/System.mjs b/packages/utils/cli/bin/core/System.mjs new file mode 100644 index 000000000..e65ff2088 --- /dev/null +++ b/packages/utils/cli/bin/core/System.mjs @@ -0,0 +1,45 @@ +import fse from 'fs-extra' +import glob from 'glob' + +export class System { + logger + + constructor({ logger }) { + this.logger = logger + } + + exit(message) { + this.logger.error(`✖ Error: ${message}\n`) + process.exit(1) + } + + getBasePath() { + return process.cwd() + } + + getPackageJSON() { + const basePath = this.getBasePath() + + const raw = fse.readFileSync(`${basePath}/package.json`).toString() + + return JSON.parse(raw) + } + + isPackageCreated(name) { + const base = this.getBasePath() + const packageJSON = this.getPackageJSON() + + return packageJSON.workspaces.some(workspace => { + const packages = glob.sync(`${base}/${workspace}/`) + + return packages.some(path => path.endsWith(`/${name}/`)) + }) + } + + writeFile({ path, content }) { + return fse + .outputFile(path, content) + .then(() => this.logger.info(`Created ${path}`)) + .catch(error => this.exit(`Failed creating ${path}`)) + } +} diff --git a/packages/utils/cli/bin/core/index.mjs b/packages/utils/cli/bin/core/index.mjs new file mode 100644 index 000000000..27151886e --- /dev/null +++ b/packages/utils/cli/bin/core/index.mjs @@ -0,0 +1,2 @@ +export { Logger } from './Logger.mjs' +export { System } from './System.mjs' diff --git a/packages/utils/cli/bin/generators/Generator.mjs b/packages/utils/cli/bin/generators/Generator.mjs new file mode 100644 index 000000000..0f5351cd7 --- /dev/null +++ b/packages/utils/cli/bin/generators/Generator.mjs @@ -0,0 +1,5 @@ +export class Generator { + execute() { + throw new Error('execute method should be implemented') + } +} diff --git a/packages/utils/cli/bin/generators/TemplateGenerator.mjs b/packages/utils/cli/bin/generators/TemplateGenerator.mjs new file mode 100644 index 000000000..e6de8f942 --- /dev/null +++ b/packages/utils/cli/bin/generators/TemplateGenerator.mjs @@ -0,0 +1,83 @@ +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' +import glob from 'glob' +import { pascalCase } from 'pascal-case' +import { camelCase } from 'camel-case' + +import { System } from '../core/index.mjs' +import { Generator } from './Generator.mjs' + +export class TemplateGenerator extends Generator { + static TYPES = { + COMPONENT: 'component', + HOOK: 'hook', + UTIL: 'util', + } + + static CONTEXTS = { + [TemplateGenerator.TYPES.COMPONENT]: 'components', + [TemplateGenerator.TYPES.HOOK]: 'components', + [TemplateGenerator.TYPES.UTIL]: 'utils', + } + + constructor({ system }) { + super() + this.system = system + } + + getDest({ type, name }) { + const basePath = this.system.getBasePath() + const context = TemplateGenerator.CONTEXTS[type] + + return `${basePath}/packages/${context}/${name}` + } + + getTemplatePaths({ type }) { + const pattern = fileURLToPath(new URL(`../../templates/${type}/**/*.js`, import.meta.url)) + + return new Promise((resolve, reject) => { + glob(pattern, async (error, paths) => { + if (error) { + return reject(error) + } + + resolve(paths) + }) + }) + } + + getTemplatePath({ path, name, type, dest }) { + const parsed = path + .replace(/(.*)\/templates\/([a-z-]+)\//, `${dest}/`) + .replaceAll(/\[|\]|\.js$/g, '') + + if (type === TemplateGenerator.TYPES.COMPONENT) { + return parsed.replace('Component', pascalCase(name)) + } + + if (type === TemplateGenerator.TYPES.HOOK) { + return parsed.replace('name', camelCase(name)) + } + + return parsed + } + + async execute({ type, name, description }) { + const dest = this.getDest({ type, name }) + const paths = await this.getTemplatePaths({ type }) + + const promises = paths.map(path => + import(path).then(module => ({ + path: this.getTemplatePath({ path, name, type, dest }), + content: module.default({ + name, + description, + }), + })) + ) + + const files = await Promise.all(promises) + + return Promise.all(files.map(file => this.system.writeFile(file))) + } +} diff --git a/packages/utils/cli/bin/generators/index.mjs b/packages/utils/cli/bin/generators/index.mjs new file mode 100644 index 000000000..ce7a2b23d --- /dev/null +++ b/packages/utils/cli/bin/generators/index.mjs @@ -0,0 +1,2 @@ +export { Generator } from './Generator.mjs' +export { TemplateGenerator } from './TemplateGenerator.mjs' diff --git a/packages/utils/cli/bin/spark-generate.mjs b/packages/utils/cli/bin/spark-generate.mjs index 0f6d7f239..4984f1eaf 100755 --- a/packages/utils/cli/bin/spark-generate.mjs +++ b/packages/utils/cli/bin/spark-generate.mjs @@ -1,156 +1,71 @@ #!/usr/bin/env node -import chalk from 'chalk' -import fse from 'fs-extra' import * as prompt from '@clack/prompts' -import { fileURLToPath } from 'node:url' -import glob from 'glob' -import { pascalCase } from 'pascal-case' -import { log, showError, writeFile } from '../utils.js' -const BASE_DIR = process.cwd() -const rawRootPackageJSON = fse.readFileSync(`${BASE_DIR}/package.json`) -let rootPackageJSON = JSON.parse(rawRootPackageJSON) +import { TemplateGenerator } from './generators/index.mjs' +import { Logger, System } from './core/index.mjs' +import { DescriptionValidator, NameValidator } from './validators/index.mjs' -const TEMPLATE_TYPE = { - COMPONENT: 'component', - HOOK: 'hook', -} - -const WORKSPACES = { - [TEMPLATE_TYPE.COMPONENT]: '/packages/components', - [TEMPLATE_TYPE.HOOK]: '/packages/hooks', -} - -const ERRORS = { - ABORT: 'Aborted package generation', - NO_PKG_NAME: 'Package name must me defined', - INVALID_PKG_NAME: 'Name name must contain letters and dash symbols only (ex: "my-package")', - INVALID_DESCRIPTION: 'Description is too short (minimum is 10 chars)', - PKG_ALREADY_EXISTS: - 'A package with that name already exists. Either delete it manually or use another name.', -} - -const packageUtils = { - /** Validate the format of the package name (kebab case format) */ - hasValidName: name => /^[a-z-]*$/.test(name), - /** Check that a package of the same name does not exists across all workspaces */ - alreadyExists: name => { - return rootPackageJSON.workspaces.some(workspace => { - const existingPackages = glob.sync(`${BASE_DIR}/${workspace}/`) - return existingPackages.some(path => path.endsWith(`/${name}/`)) - }) - }, - /** Retrieves the target folder of the generated package */ - getDirectory: (name, template) => `${WORKSPACES[template]}/${name}/`, - /** Retrieves the full path to the folder of the generated package */ - getFullPath: (name, template) => `${BASE_DIR}${packageUtils.getDirectory(name, template)}`, -} +const logger = new Logger() +const system = new System({ logger }) +const generator = new TemplateGenerator({ system }) -async function promptPackageName() { +export const run = async () => { const name = await prompt.text({ message: 'Package name (must contain letters and dash symbols only):', initialValue: '', validate(value) { - if (value == null) return ERRORS.NO_PKG_NAME - if (!packageUtils.hasValidName(value)) return ERRORS.INVALID_PKG_NAME - if (packageUtils.alreadyExists(value)) return ERRORS.PKG_ALREADY_EXISTS + const validator = new NameValidator({ system }) + + return validator.validate(value) }, }) - if (prompt.isCancel(name)) showError(ERRORS.ABORT) - - return name -} + if (prompt.isCancel(name)) { + system.exit('Aborted package generation') + } -async function promptPackageTemplate() { - const template = await prompt.select({ + const type = await prompt.select({ message: 'Chose a template:', - initialValue: TEMPLATE_TYPE.COMPONENT, + initialValue: 'component', options: [ { - value: TEMPLATE_TYPE.COMPONENT, + value: TemplateGenerator.TYPES.COMPONENT, label: 'Component', - hint: 'Typescript dummy component with some tests, stories and config files', + hint: 'TypeScript component package', }, { - value: TEMPLATE_TYPE.HOOK, + value: TemplateGenerator.TYPES.HOOK, label: 'Hook', - hint: 'Typescript hook with some tests, stories and config files', + hint: 'TypeScript hook package', + }, + { + value: TemplateGenerator.TYPES.UTIL, + label: 'Utility', + hint: 'TypeScript utility package', }, ], }) - if (prompt.isCancel(template)) showError(ERRORS.ABORT) - - return template -} + if (prompt.isCancel(type)) { + system.exit('Aborted package generation') + } -async function promptPackageDescription() { const description = await prompt.text({ message: 'Describe your package (short description):', initialValue: '', validate(value) { - if (!value) return `You package must have a description` - if (value.length < 10) return ERRORS.INVALID_DESCRIPTION + const validator = new DescriptionValidator() + + return validator.validate(value) }, }) - if (prompt.isCancel(description)) showError(ERRORS.ABORT) - - return description -} - -/** - * Program starts here - */ -prompt.intro(`Generate Spark package`) - -const name = await promptPackageName() -const template = await promptPackageTemplate() -const description = await promptPackageDescription() - -const packagePath = packageUtils.getFullPath(name, template) - -switch (template) { - case TEMPLATE_TYPE.COMPONENT: - generateComponentPackage(name, description) - break - case TEMPLATE_TYPE.HOOK: - generateHookPackage(name, description) - break -} - -prompt.outro(`Generating package...`) - -function generateComponentPackage(name, description) { - const templatesPattern = fileURLToPath(new URL('../templates/**/*.js', import.meta.url)) - - glob(templatesPattern, async (err, res) => { - if (err) showError(err) - if (res) { - const templateContents = res.map(templatePath => - import(templatePath).then(module => ({ - path: templatePath - .replace(/(.*)\/templates\//, packagePath) - .replace('Component', pascalCase(name)) - .replaceAll(/\[|\]|\.js$/g, ''), - content: module.default({ - component: name, - description: description, - }), - })) - ) + if (prompt.isCancel(description)) { + system.exit('Aborted package generation') + } - const filesToWrite = await Promise.all(templateContents) - - Promise.all(filesToWrite.map(writeFile)).then(() => { - log.success('All package files has been properly written!') - }) - } - }) + generator.execute({ name, type, description }) } -function generateHookPackage(name, description) { - showError('Todo: template for hook packages is not ready yet.') -} +run() diff --git a/packages/utils/cli/bin/spark-setup-theme.mjs b/packages/utils/cli/bin/spark-setup-theme.mjs index 8928b0bd8..267728977 100755 --- a/packages/utils/cli/bin/spark-setup-theme.mjs +++ b/packages/utils/cli/bin/spark-setup-theme.mjs @@ -5,7 +5,8 @@ import { join, extname, parse, sep } from 'path' import { readFileSync, readdirSync, writeFileSync, unlinkSync } from 'fs' import { transformSync } from 'esbuild' -import { log, showError } from '../utils.js' +const logger = new Logger() +const system = new System({ logger }) const jsFileExtension = '.js' @@ -14,7 +15,7 @@ const configFile = readdirSync(process.cwd()).find(fileName => ) if (!configFile) { - showError( + system.exit( "We couldn't find a `spark.theme.config` file in this folder. Please make sure that the file is located in the root folder of your project" ) } @@ -24,8 +25,9 @@ const filePath = join(process.cwd(), configFile) const allowedExtensions = ['.ts', '.mts', '.cts', '.js', '.cjs', '.mjs'] const fileExtension = extname(filePath) + if (!allowedExtensions.includes(fileExtension)) { - showError(`Your spark.theme.config file extension (${fileExtension}) is not supported.`) + system.exit(`Your spark.theme.config file extension (${fileExtension}) is not supported.`) } const tsCode = readFileSync(filePath, 'utf-8') @@ -42,6 +44,6 @@ const child = spawn(process.execPath, [jsFilePath], { child.on('exit', code => { if (!configFileIsInJS) unlinkSync(jsFilePath) - log.success('✨ Your Spark theme config files have been successfully created!') + logger.success('✨ Your Spark theme config files have been successfully created!') process.exit(code) }) diff --git a/packages/utils/cli/bin/validators/DescriptionValidator.mjs b/packages/utils/cli/bin/validators/DescriptionValidator.mjs new file mode 100644 index 000000000..851d837a9 --- /dev/null +++ b/packages/utils/cli/bin/validators/DescriptionValidator.mjs @@ -0,0 +1,19 @@ +import { Validator } from './Validator.mjs' + +export class DescriptionValidator extends Validator { + constructor() { + super() + } + + validate(description) { + if (!description) { + return 'You package must have a description' + } + + if (description.length < 10) { + return 'Description is too short (minimum is 10 chars)' + } + + return undefined + } +} diff --git a/packages/utils/cli/bin/validators/NameValidator.mjs b/packages/utils/cli/bin/validators/NameValidator.mjs new file mode 100644 index 000000000..64e4e878e --- /dev/null +++ b/packages/utils/cli/bin/validators/NameValidator.mjs @@ -0,0 +1,25 @@ +import { System } from '../core/index.mjs' +import { Validator } from './Validator.mjs' + +export class NameValidator extends Validator { + system + + constructor({ system }) { + super() + this.system = system + } + + validate(name) { + if (!name) { + return 'Package name must me defined' + } + + if (!/^[a-z-]*$/.test(name)) { + return 'Name name must contain letters and dash symbols only (ex: "my-package")' + } + + if (this.system.isPackageCreated(name)) { + return 'A package with that name already exists. Either delete it manually or use another name.' + } + } +} diff --git a/packages/utils/cli/bin/validators/Validator.mjs b/packages/utils/cli/bin/validators/Validator.mjs new file mode 100644 index 000000000..b4de9ed83 --- /dev/null +++ b/packages/utils/cli/bin/validators/Validator.mjs @@ -0,0 +1,5 @@ +export class Validator { + validate() { + throw new Error('validate method should be implemented') + } +} diff --git a/packages/utils/cli/bin/validators/index.mjs b/packages/utils/cli/bin/validators/index.mjs new file mode 100644 index 000000000..3a5e0737b --- /dev/null +++ b/packages/utils/cli/bin/validators/index.mjs @@ -0,0 +1,2 @@ +export { NameValidator } from './NameValidator.mjs' +export { DescriptionValidator } from './DescriptionValidator.mjs' diff --git a/packages/utils/cli/package.json b/packages/utils/cli/package.json index 2fde06b02..10a8ca08b 100644 --- a/packages/utils/cli/package.json +++ b/packages/utils/cli/package.json @@ -18,11 +18,12 @@ "esbuild": "0.17.8", "fs-extra": "11.1.0", "glob": "8.1.0", - "pascal-case": "3.1.2" + "pascal-case": "3.1.2", + "camel-case": "4.1.2" }, "repository": { "type": "git", "url": "git@github.com:adevinta/spark.git", "directory": "packages/utils/cli" } -} +} \ No newline at end of file diff --git a/packages/utils/cli/templates/[.npmignore].js b/packages/utils/cli/templates/component/[.npmignore].js similarity index 100% rename from packages/utils/cli/templates/[.npmignore].js rename to packages/utils/cli/templates/component/[.npmignore].js diff --git a/packages/utils/cli/templates/[package.json].js b/packages/utils/cli/templates/component/[package.json].js similarity index 74% rename from packages/utils/cli/templates/[package.json].js rename to packages/utils/cli/templates/component/[package.json].js index aa90cb490..14a4ad45f 100644 --- a/packages/utils/cli/templates/[package.json].js +++ b/packages/utils/cli/templates/component/[package.json].js @@ -1,5 +1,5 @@ -export default ({ component, description }) => `{ - "name": "@spark-ui/${component}", +export default ({ name, description }) => `{ + "name": "@spark-ui/${name}", "version": "1.0.0", "description": "${description}", "publishConfig": { diff --git a/packages/utils/cli/templates/[tsconfig.json].js b/packages/utils/cli/templates/component/[tsconfig.json].js similarity index 100% rename from packages/utils/cli/templates/[tsconfig.json].js rename to packages/utils/cli/templates/component/[tsconfig.json].js diff --git a/packages/utils/cli/templates/[vite.config.ts].js b/packages/utils/cli/templates/component/[vite.config.ts].js similarity index 100% rename from packages/utils/cli/templates/[vite.config.ts].js rename to packages/utils/cli/templates/component/[vite.config.ts].js diff --git a/packages/utils/cli/templates/src/[Component.stories.mdx].js b/packages/utils/cli/templates/component/src/[Component.stories.mdx].js similarity index 78% rename from packages/utils/cli/templates/src/[Component.stories.mdx].js rename to packages/utils/cli/templates/component/src/[Component.stories.mdx].js index 34bbf7f6a..799cb7c6b 100644 --- a/packages/utils/cli/templates/src/[Component.stories.mdx].js +++ b/packages/utils/cli/templates/component/src/[Component.stories.mdx].js @@ -1,7 +1,7 @@ import { pascalCase } from 'pascal-case' -export default ({ component, description }) => { - const componentName = pascalCase(component) +export default ({ name, description }) => { + const componentName = pascalCase(name) return `import { ArgsTable, Meta, Story } from '@storybook/addon-docs' import { ReactLiveBlock } from '@docs/helpers/ReactLiveBlock' @@ -20,13 +20,13 @@ ${description} \`\`\` -npm install @spark-ui/${component} +npm install @spark-ui/${name} \`\`\` \`\`\` -import { ${componentName} } from "@spark-ui/${component}" +import { ${componentName} } from "@spark-ui/${name}" \`\`\` diff --git a/packages/utils/cli/templates/src/[Component.stories.tsx].js b/packages/utils/cli/templates/component/src/[Component.stories.tsx].js similarity index 76% rename from packages/utils/cli/templates/src/[Component.stories.tsx].js rename to packages/utils/cli/templates/component/src/[Component.stories.tsx].js index 164ae63ea..4987d6517 100644 --- a/packages/utils/cli/templates/src/[Component.stories.tsx].js +++ b/packages/utils/cli/templates/component/src/[Component.stories.tsx].js @@ -1,7 +1,7 @@ import { pascalCase } from 'pascal-case' -export default ({ component, description }) => { - const componentName = pascalCase(component) +export default ({ name, description }) => { + const componentName = pascalCase(name) return `import { ReactLiveBlock } from '@docs/helpers/ReactLiveBlock' diff --git a/packages/utils/cli/templates/src/[Component.test.tsx].js b/packages/utils/cli/templates/component/src/[Component.test.tsx].js similarity index 90% rename from packages/utils/cli/templates/src/[Component.test.tsx].js rename to packages/utils/cli/templates/component/src/[Component.test.tsx].js index de96fa879..d25000ebb 100644 --- a/packages/utils/cli/templates/src/[Component.test.tsx].js +++ b/packages/utils/cli/templates/component/src/[Component.test.tsx].js @@ -1,7 +1,7 @@ import { pascalCase } from 'pascal-case' -export default ({ component }) => { - const componentName = pascalCase(component) +export default ({ name }) => { + const componentName = pascalCase(name) return `import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' diff --git a/packages/utils/cli/templates/src/[Component.tsx].js b/packages/utils/cli/templates/component/src/[Component.tsx].js similarity index 79% rename from packages/utils/cli/templates/src/[Component.tsx].js rename to packages/utils/cli/templates/component/src/[Component.tsx].js index 05a6710b6..be53c6ba5 100644 --- a/packages/utils/cli/templates/src/[Component.tsx].js +++ b/packages/utils/cli/templates/component/src/[Component.tsx].js @@ -1,7 +1,7 @@ import { pascalCase } from 'pascal-case' -export default ({ component }) => { - const componentName = pascalCase(component) +export default ({ name }) => { + const componentName = pascalCase(name) return `import { ComponentPropsWithoutRef, PropsWithChildren } from 'react' diff --git a/packages/utils/cli/templates/component/src/[Component.variants.tsx].js b/packages/utils/cli/templates/component/src/[Component.variants.tsx].js new file mode 100644 index 000000000..a79a5062e --- /dev/null +++ b/packages/utils/cli/templates/component/src/[Component.variants.tsx].js @@ -0,0 +1,8 @@ +import { pascalCase } from 'pascal-case' + +export default ({ name }) => { + const componentName = pascalCase(name) + + return ` +` +} diff --git a/packages/utils/cli/templates/src/[index.ts].js b/packages/utils/cli/templates/component/src/[index.ts].js similarity index 57% rename from packages/utils/cli/templates/src/[index.ts].js rename to packages/utils/cli/templates/component/src/[index.ts].js index 13dcf8788..a86a7d2c1 100644 --- a/packages/utils/cli/templates/src/[index.ts].js +++ b/packages/utils/cli/templates/component/src/[index.ts].js @@ -1,7 +1,7 @@ import { pascalCase } from 'pascal-case' -export default ({ component }) => { - const componentName = pascalCase(component) +export default ({ name }) => { + const componentName = pascalCase(name) return `export { ${componentName} } from './${componentName}' ` diff --git a/packages/utils/cli/templates/hook/[.npmignore].js b/packages/utils/cli/templates/hook/[.npmignore].js new file mode 100644 index 000000000..5446b8c3b --- /dev/null +++ b/packages/utils/cli/templates/hook/[.npmignore].js @@ -0,0 +1,3 @@ +export default () => `src +**/*.stories.* +` diff --git a/packages/utils/cli/templates/hook/[package.json].js b/packages/utils/cli/templates/hook/[package.json].js new file mode 100644 index 000000000..14a4ad45f --- /dev/null +++ b/packages/utils/cli/templates/hook/[package.json].js @@ -0,0 +1,15 @@ +export default ({ name, description }) => `{ + "name": "@spark-ui/${name}", + "version": "1.0.0", + "description": "${description}", + "publishConfig": { + "access": "public" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "scripts": { + "build": "vite build" + } +} +` diff --git a/packages/utils/cli/templates/hook/[tsconfig.json].js b/packages/utils/cli/templates/hook/[tsconfig.json].js new file mode 100644 index 000000000..dffccabfb --- /dev/null +++ b/packages/utils/cli/templates/hook/[tsconfig.json].js @@ -0,0 +1,4 @@ +export default () => `{ + "extends": "../../../tsconfig.json", + "include": ["src/**/*", "../../../global.d.ts"] +}` diff --git a/packages/utils/cli/templates/hook/[vite.config.ts].js b/packages/utils/cli/templates/hook/[vite.config.ts].js new file mode 100644 index 000000000..e1378874c --- /dev/null +++ b/packages/utils/cli/templates/hook/[vite.config.ts].js @@ -0,0 +1,7 @@ +export default () => `import path from 'path' +import { getComponentConfiguration } from '../../../config/index' + +const { name } = require(path.resolve(__dirname, 'package.json')) + +export default getComponentConfiguration(name) +` diff --git a/packages/utils/cli/templates/hook/src/[index.ts].js b/packages/utils/cli/templates/hook/src/[index.ts].js new file mode 100644 index 000000000..bcb4b0415 --- /dev/null +++ b/packages/utils/cli/templates/hook/src/[index.ts].js @@ -0,0 +1,8 @@ +import { camelCase } from 'camel-case' + +export default ({ name }) => { + const hookName = camelCase(name) + + return `export { ${hookName} } from './${hookName}' +` +} diff --git a/packages/utils/cli/templates/hook/src/[name.stories.mdx].js b/packages/utils/cli/templates/hook/src/[name.stories.mdx].js new file mode 100644 index 000000000..10fac8de3 --- /dev/null +++ b/packages/utils/cli/templates/hook/src/[name.stories.mdx].js @@ -0,0 +1,42 @@ +import { camelCase } from 'camel-case' + +export default ({ name, description }) => { + const hookName = camelCase(name) + + return `import { ArgsTable, Meta, Story } from '@storybook/addon-docs' +import { ReactLiveBlock } from '@docs/helpers/ReactLiveBlock' +import { StoryHeading } from '@docs/helpers/StoryHeading' + +import { ${hookName} } from '.' + +import * as stories from './${hookName}.stories' + + + +# ${hookName} + +${description} + + + +\`\`\` +npm install @spark-ui/${name} +\`\`\` + + + +\`\`\` +import { ${hookName} } from "@spark-ui/${name}" +\`\`\` + + + + + +\`\`\`jsx +import { ${hookName} } from "@spark-ui/${name}" + +const Demo = () => {} +\`\`\` +` +} diff --git a/packages/utils/cli/templates/hook/src/[name.test.tsx].js b/packages/utils/cli/templates/hook/src/[name.test.tsx].js new file mode 100644 index 000000000..920d64e80 --- /dev/null +++ b/packages/utils/cli/templates/hook/src/[name.test.tsx].js @@ -0,0 +1,17 @@ +import { camelCase } from 'camel-case' + +export default ({ name }) => { + const hookName = camelCase(name) + + return `import { renderHook } from '@testing-library/react' +import { describe, expect, it } from 'vitest' + +import { ${hookName} } from './${hookName}' + +describe('${hookName}', () => { + it('should be defined', () => { + expect(${hookName}).toBeDefined() + }) +}) +` +} diff --git a/packages/utils/cli/templates/hook/src/[name.tsx].js b/packages/utils/cli/templates/hook/src/[name.tsx].js new file mode 100644 index 000000000..bf0ba8051 --- /dev/null +++ b/packages/utils/cli/templates/hook/src/[name.tsx].js @@ -0,0 +1,10 @@ +import { camelCase } from 'camel-case' + +export default ({ name }) => { + const hookName = camelCase(name) + + return `export function ${hookName}() { + return null +} +` +} diff --git a/packages/utils/cli/templates/src/[Component.variants.tsx].js b/packages/utils/cli/templates/src/[Component.variants.tsx].js deleted file mode 100644 index 1ccbfb880..000000000 --- a/packages/utils/cli/templates/src/[Component.variants.tsx].js +++ /dev/null @@ -1,8 +0,0 @@ -import { pascalCase } from 'pascal-case' - -export default ({ component }) => { - const componentName = pascalCase(component) - - return ` -` -} diff --git a/packages/utils/cli/templates/util/[.npmignore].js b/packages/utils/cli/templates/util/[.npmignore].js new file mode 100644 index 000000000..b71019159 --- /dev/null +++ b/packages/utils/cli/templates/util/[.npmignore].js @@ -0,0 +1 @@ +export default () => 'src' diff --git a/packages/utils/cli/templates/util/[package.json].js b/packages/utils/cli/templates/util/[package.json].js new file mode 100644 index 000000000..14a4ad45f --- /dev/null +++ b/packages/utils/cli/templates/util/[package.json].js @@ -0,0 +1,15 @@ +export default ({ name, description }) => `{ + "name": "@spark-ui/${name}", + "version": "1.0.0", + "description": "${description}", + "publishConfig": { + "access": "public" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "scripts": { + "build": "vite build" + } +} +` diff --git a/packages/utils/cli/templates/util/[tsconfig.json].js b/packages/utils/cli/templates/util/[tsconfig.json].js new file mode 100644 index 000000000..7e69de2d5 --- /dev/null +++ b/packages/utils/cli/templates/util/[tsconfig.json].js @@ -0,0 +1,5 @@ +export default () => `{ + "extends": "../../../tsconfig.json", + "include": ["src/**/*", "../../global.d.ts"] +} +` diff --git a/packages/utils/cli/templates/util/[vite.config.ts].js b/packages/utils/cli/templates/util/[vite.config.ts].js new file mode 100644 index 000000000..d5ea037d4 --- /dev/null +++ b/packages/utils/cli/templates/util/[vite.config.ts].js @@ -0,0 +1,23 @@ +export default () => `import { terser } from 'rollup-plugin-terser' +import dts from 'vite-plugin-dts' + +export default { + build: { + target: 'es2015', + lib: { + entry: 'src/index.ts', + formats: ['es', 'cjs'], + fileName: 'index', + }, + rollupOptions: { + external: ['node:path', 'node:fs'], + plugins: [terser()], + }, + }, + plugins: [ + dts({ + entryRoot: './src', + }), + ], +} +` diff --git a/packages/utils/cli/templates/util/src/[index.ts].js b/packages/utils/cli/templates/util/src/[index.ts].js new file mode 100644 index 000000000..85cc77b58 --- /dev/null +++ b/packages/utils/cli/templates/util/src/[index.ts].js @@ -0,0 +1 @@ +export default () => ''