2022-06-17 18:23:17 +02:00
|
|
|
|
import fs from 'fs'
|
|
|
|
|
import path from 'path'
|
|
|
|
|
import prompts from 'prompts'
|
2022-06-16 17:11:31 +02:00
|
|
|
|
import chalk from 'chalk'
|
2022-06-17 18:23:17 +02:00
|
|
|
|
import { banner } from './banner.mjs'
|
|
|
|
|
import mustache from 'mustache'
|
|
|
|
|
import { execSync } from 'child_process'
|
2023-10-04 09:14:18 +02:00
|
|
|
|
import languages from '../config/languages.json' assert { type: 'json' }
|
2022-06-16 17:11:31 +02:00
|
|
|
|
// Software
|
2022-08-27 12:02:07 -07:00
|
|
|
|
import designs from '../config/software/designs.json' assert { type: 'json' }
|
2023-04-18 13:13:08 +00:00
|
|
|
|
import plugins from '../config/software/plugins.json' assert { type: 'json' }
|
2022-06-16 17:11:31 +02:00
|
|
|
|
|
2022-06-17 18:23:17 +02:00
|
|
|
|
const type = process.argv[2]
|
|
|
|
|
|
|
|
|
|
// Add new design
|
|
|
|
|
if (type === 'design') {
|
|
|
|
|
console.clear()
|
|
|
|
|
console.log(banner)
|
|
|
|
|
addDesign()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add new plugin
|
|
|
|
|
else if (type === 'plugin') {
|
|
|
|
|
console.clear()
|
|
|
|
|
console.log(banner)
|
|
|
|
|
addPlugin()
|
2022-10-02 13:11:29 -07:00
|
|
|
|
} else
|
|
|
|
|
console.log(`
|
2022-06-17 18:23:17 +02:00
|
|
|
|
Usage:
|
|
|
|
|
|
|
|
|
|
${chalk.bold.blue('yarn new design')} 👉 Adds a new design
|
|
|
|
|
${chalk.bold.blue('yarn new plugin')} 👉 Adds a new plugin
|
|
|
|
|
${chalk.bold.blue('yarn new')} ${chalk.yellow('[anything else]')} 👉 Shows this help
|
|
|
|
|
`)
|
|
|
|
|
|
|
|
|
|
async function addDesign() {
|
|
|
|
|
console.log(`
|
|
|
|
|
|
|
|
|
|
${chalk.bold.yellow('👕 Add a new design')}
|
|
|
|
|
${chalk.gray('≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡')}
|
|
|
|
|
|
|
|
|
|
We're going to add a new design to this repository. That's awesome 🎉
|
2023-10-04 09:14:18 +02:00
|
|
|
|
Let's start by picking a name. Naming things is hard 😬
|
2022-06-17 18:23:17 +02:00
|
|
|
|
|
|
|
|
|
We'd appreciate if you pick:
|
|
|
|
|
|
2022-10-02 13:11:29 -07:00
|
|
|
|
- a firstname like ${chalk.green('alex')}, ${chalk.green('jordan')}, ${chalk.green(
|
2023-10-04 09:14:18 +02:00
|
|
|
|
'ezra'
|
|
|
|
|
)}, or ${chalk.green('logan')}
|
2022-10-02 13:11:29 -07:00
|
|
|
|
- that is an aliteration with the kind of design, like ${chalk.green(
|
|
|
|
|
'wahid'
|
|
|
|
|
)} for a ${chalk.green('w')}aistcoat
|
2022-06-17 18:23:17 +02:00
|
|
|
|
|
2023-10-20 15:45:31 +02:00
|
|
|
|
Bonus points for picking a name that embraces diversity 🌈 ✊🏾
|
2022-06-17 18:23:17 +02:00
|
|
|
|
`)
|
|
|
|
|
|
|
|
|
|
const { name } = await prompts({
|
|
|
|
|
type: 'text',
|
|
|
|
|
name: 'name',
|
2023-05-27 10:14:30 -07:00
|
|
|
|
message: 'What name would you like the design to have? ([a-z0-9_] only)',
|
2023-04-18 13:20:40 +00:00
|
|
|
|
validate: validateDesignName,
|
2022-06-17 18:23:17 +02:00
|
|
|
|
})
|
|
|
|
|
|
2023-10-04 09:14:18 +02:00
|
|
|
|
if (name) {
|
|
|
|
|
console.log('\n' + ` Alright, let's add ${chalk.green(name)} 🪄`)
|
|
|
|
|
createDesign(name)
|
2022-10-02 13:11:29 -07:00
|
|
|
|
execSync('npm run reconfigure')
|
2023-10-20 15:45:31 +02:00
|
|
|
|
console.log(` Installing & linking dependencies...`)
|
|
|
|
|
execSync('yarn install')
|
2022-06-17 18:23:17 +02:00
|
|
|
|
console.log(` All done 🎉`)
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
console.log(`
|
|
|
|
|
|
|
|
|
|
${chalk.bold.yellow('✨ Summary')}
|
|
|
|
|
${chalk.gray('≡≡≡≡≡≡≡≡≡≡')}
|
|
|
|
|
|
2022-10-02 13:11:29 -07:00
|
|
|
|
👉 We've created your design skeleton at ${chalk.green('designs/' + name)}
|
2023-04-18 18:47:26 +00:00
|
|
|
|
👉 We've configured the packages via the ${chalk.green('package.json')} file
|
2022-10-02 13:11:29 -07:00
|
|
|
|
👉 We've added ${chalk.green('designs/' + name)} to the lab
|
2022-06-17 18:23:17 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
${chalk.bold.yellow('✏️ Make it your own')}
|
|
|
|
|
${chalk.gray('≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡')}
|
|
|
|
|
|
|
|
|
|
Hhere's a few other things you can configure:
|
|
|
|
|
|
2022-10-02 13:11:29 -07:00
|
|
|
|
👉 ${chalk.yellow('Author')}: Credit where credit is due; Add yourself as author in ${chalk.green(
|
2023-10-04 09:14:18 +02:00
|
|
|
|
'config/exceptions.yaml'
|
|
|
|
|
)}
|
|
|
|
|
👉 ${chalk.yellow('Description')}: We used placeholder metadata; Update it in ${chalk.green(
|
|
|
|
|
'config/software/designs.json'
|
|
|
|
|
)}
|
2022-10-02 13:11:29 -07:00
|
|
|
|
👉 ${chalk.yellow(
|
|
|
|
|
'Dependencies'
|
|
|
|
|
)}: If you need additional plugins or patterns to extend, update ${chalk.green(
|
2023-10-04 09:14:18 +02:00
|
|
|
|
'config/dependencies.yaml'
|
|
|
|
|
)}
|
2022-06-17 18:23:17 +02:00
|
|
|
|
|
|
|
|
|
If you change any of these, run ${chalk.blue('yarn reconfigure')} to update the package(s).
|
|
|
|
|
|
|
|
|
|
|
2023-10-22 10:06:27 +02:00
|
|
|
|
${chalk.bold.yellow('⚒️ Build packages at least once')}
|
|
|
|
|
${chalk.gray('≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡')}
|
|
|
|
|
|
|
|
|
|
⚠️ You need to run ${chalk.blue('yarn buildall')} at least once before you run the steps below.
|
|
|
|
|
${chalk.gray(`If you have never built the packages (for example, you have just cloned this repo),
|
|
|
|
|
do this now. You only need to re-run this step when you make changes to core plugins`)}
|
|
|
|
|
|
|
|
|
|
|
2022-06-17 18:23:17 +02:00
|
|
|
|
${chalk.bold.yellow('👷 Get to work')}
|
|
|
|
|
${chalk.gray('≡≡≡≡≡≡≡≡≡≡≡≡≡≡')}
|
|
|
|
|
|
2023-10-22 10:06:27 +02:00
|
|
|
|
🚀 You can now start the development environment with ${chalk.blue('yarn lab')}
|
2022-06-17 18:23:17 +02:00
|
|
|
|
📖 Documentation is available at ${chalk.green('https://freesewing.dev/')}
|
|
|
|
|
🤓 Happy hacking
|
|
|
|
|
|
|
|
|
|
`)
|
2022-10-02 13:11:29 -07:00
|
|
|
|
} catch (err) {
|
2022-06-17 18:23:17 +02:00
|
|
|
|
console.log(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function addPlugin() {
|
|
|
|
|
console.log(`
|
|
|
|
|
|
2023-04-18 13:13:08 +00:00
|
|
|
|
${chalk.bold.yellow('👕 Add a new plugin')}
|
|
|
|
|
${chalk.gray('≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡')}
|
2022-06-17 18:23:17 +02:00
|
|
|
|
|
2023-04-18 13:13:08 +00:00
|
|
|
|
We're going to add a new plugin to this repository. That's awesome 🎉
|
|
|
|
|
Let's start by picking the name for this plugin 🏷️
|
|
|
|
|
Try to keep it to one word that explains what the plugin does e.g. ${chalk.green(
|
|
|
|
|
'flip'
|
|
|
|
|
)}, ${chalk.green('mirror')},
|
|
|
|
|
${chalk.green('round')}.
|
2022-06-17 18:23:17 +02:00
|
|
|
|
|
|
|
|
|
`)
|
2023-04-18 13:13:08 +00:00
|
|
|
|
|
|
|
|
|
const { name } = await prompts({
|
|
|
|
|
type: 'text',
|
|
|
|
|
name: 'name',
|
|
|
|
|
message: 'What name would you like the plugin to have? ([a-z] only)',
|
2023-04-18 13:20:40 +00:00
|
|
|
|
validate: validatePluginName,
|
2023-04-18 13:13:08 +00:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (name) {
|
|
|
|
|
console.log('\n' + ` Alright, let's add ${chalk.green(name)} to plugins 🪄`)
|
|
|
|
|
createPlugin(name)
|
|
|
|
|
execSync('npm run reconfigure')
|
|
|
|
|
console.log(` All done 🎉`)
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
console.log(`
|
|
|
|
|
|
|
|
|
|
${chalk.bold.yellow('✨ Summary')}
|
|
|
|
|
${chalk.gray('≡≡≡≡≡≡≡≡≡≡')}
|
|
|
|
|
|
|
|
|
|
👉 We've created your plugin skeleton at ${chalk.green('plugins/plugin-' + name)}
|
2023-04-18 18:47:26 +00:00
|
|
|
|
👉 We've configured the packages via the ${chalk.green('package.json')} file
|
2023-04-18 13:13:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
${chalk.bold.yellow('✏️ Make it your own')}
|
|
|
|
|
${chalk.gray('≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡')}
|
|
|
|
|
|
|
|
|
|
Hhere's a few other things you can configure:
|
|
|
|
|
|
|
|
|
|
👉 ${chalk.yellow('Author')}: Credit where credit is due; Add yourself as author in ${chalk.green(
|
2023-10-04 09:14:18 +02:00
|
|
|
|
'config/exceptions.yaml'
|
|
|
|
|
)}
|
2023-04-18 13:13:08 +00:00
|
|
|
|
👉 ${chalk.yellow('Description')}: We used a placeholder description; Update it in ${chalk.green(
|
2023-10-04 09:14:18 +02:00
|
|
|
|
'config/software/plugins.json'
|
|
|
|
|
)}
|
2023-04-18 13:13:08 +00:00
|
|
|
|
👉 ${chalk.yellow(
|
|
|
|
|
'Dependencies'
|
|
|
|
|
)}: If you need additional plugins or patterns to extend, update ${chalk.green(
|
2023-10-04 09:14:18 +02:00
|
|
|
|
'config/dependencies.yaml'
|
|
|
|
|
)}
|
2023-04-18 13:13:08 +00:00
|
|
|
|
|
|
|
|
|
If you change any of these, run ${chalk.blue('yarn reconfigure')} to update the package(s).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
${chalk.bold.yellow('👷 Get to work')}
|
|
|
|
|
${chalk.gray('≡≡≡≡≡≡≡≡≡≡≡≡≡≡')}
|
|
|
|
|
|
|
|
|
|
🛠️ You can now start the development environment with ${chalk.blue('yarn lab')}
|
|
|
|
|
📖 Documentation is available at ${chalk.green('https://freesewing.dev/')}
|
|
|
|
|
🤓 Happy hacking
|
|
|
|
|
|
|
|
|
|
`)
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.log(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-17 18:23:17 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-18 13:20:40 +00:00
|
|
|
|
function validateDesignName(name) {
|
2023-10-04 09:14:18 +02:00
|
|
|
|
if (Object.keys(designs).indexOf(name) !== -1)
|
2022-10-02 13:11:29 -07:00
|
|
|
|
return `Sorry but ${name} is already taken so you'll need to pick something else`
|
2022-06-17 18:23:17 +02:00
|
|
|
|
|
2023-05-27 10:14:30 -07:00
|
|
|
|
if (/^([a-z][a-z0-9_]*)$/.test(name)) return true
|
2023-05-26 20:55:56 -07:00
|
|
|
|
else
|
2023-05-27 10:14:30 -07:00
|
|
|
|
return ' 🙈 Please use only lowercase letters, digits, or underscores. Names must start with a lowercase letter. 🤷'
|
2022-06-17 18:23:17 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-18 13:20:40 +00:00
|
|
|
|
function validatePluginName(name) {
|
2023-04-18 13:13:08 +00:00
|
|
|
|
const pluginName = 'plugin-' + name
|
|
|
|
|
if ([...Object.keys(plugins)].indexOf(pluginName) !== -1)
|
|
|
|
|
return `Sorry but ${pluginName} is already taken so you'll need to pick something else`
|
|
|
|
|
|
|
|
|
|
if (/^([a-z]+)$/.test(name)) return true
|
|
|
|
|
else return ' 🙈 Please use only [a-z], no spaces, no capitals, no nothing 🤷'
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-04 09:14:18 +02:00
|
|
|
|
function createDesign(name) {
|
2022-06-17 18:23:17 +02:00
|
|
|
|
const template = ['config', 'templates', 'design']
|
|
|
|
|
const design = ['designs', name]
|
2023-04-18 13:29:54 +00:00
|
|
|
|
const description = 'A FreeSewing pattern that needs a description'
|
2022-10-02 13:11:29 -07:00
|
|
|
|
const capitalized_name = name.charAt(0).toUpperCase() + name.slice(1)
|
2022-06-17 18:23:17 +02:00
|
|
|
|
|
|
|
|
|
// Add to designs config file
|
2023-10-04 09:14:18 +02:00
|
|
|
|
designs[name] = {
|
2022-10-02 13:11:29 -07:00
|
|
|
|
code: 'Coder name',
|
2023-10-20 15:45:31 +02:00
|
|
|
|
description: description,
|
2022-10-02 13:11:29 -07:00
|
|
|
|
design: 'Designer name',
|
|
|
|
|
difficulty: 1,
|
2023-10-20 15:45:31 +02:00
|
|
|
|
lab: true,
|
|
|
|
|
org: false,
|
2022-10-02 13:11:29 -07:00
|
|
|
|
tags: ['tagname'],
|
2023-10-04 09:14:18 +02:00
|
|
|
|
techniques: ['techname'],
|
2022-10-02 13:11:29 -07:00
|
|
|
|
}
|
|
|
|
|
write(['config', 'software', 'designs.json'], JSON.stringify(orderDesigns(designs), null, 2))
|
2022-06-17 18:23:17 +02:00
|
|
|
|
|
|
|
|
|
// Create folders
|
|
|
|
|
mkdir([...design, 'src'])
|
2023-10-04 09:14:18 +02:00
|
|
|
|
mkdir([...design, 'i18n'])
|
2022-06-17 18:23:17 +02:00
|
|
|
|
mkdir([...design, 'tests'])
|
|
|
|
|
|
|
|
|
|
// Create package.json
|
2022-10-13 16:19:36 +02:00
|
|
|
|
templateOut([...template, 'package.json.mustache'], [...design, 'package.json'], {
|
|
|
|
|
name,
|
|
|
|
|
description,
|
|
|
|
|
})
|
2022-06-17 18:23:17 +02:00
|
|
|
|
|
2023-10-04 09:14:18 +02:00
|
|
|
|
// Create src/index.mjs
|
2022-10-13 16:19:36 +02:00
|
|
|
|
templateOut([...template, 'src', 'index.mjs.mustache'], [...design, 'src', 'index.mjs'], {
|
2022-10-02 13:11:29 -07:00
|
|
|
|
capitalized_name,
|
|
|
|
|
})
|
2022-06-17 18:23:17 +02:00
|
|
|
|
|
2023-10-04 09:14:18 +02:00
|
|
|
|
// Copy i18n/index.mjs
|
|
|
|
|
cp([...template, 'i18n', 'index.mjs'], [...design, 'i18n', 'index.mjs'])
|
|
|
|
|
|
|
|
|
|
// Create i18n translation files
|
|
|
|
|
for (const language of languages)
|
|
|
|
|
templateOut([...template, 'i18n', 'en.json'], [...design, 'i18n', `${language}.json`], {
|
|
|
|
|
title: capitalized_name,
|
|
|
|
|
description,
|
|
|
|
|
})
|
|
|
|
|
|
2022-06-17 18:23:17 +02:00
|
|
|
|
// Create tests file
|
2022-10-02 13:11:29 -07:00
|
|
|
|
cp([...template, 'tests', 'shared.test.mjs'], [...design, 'tests', 'shared.test.mjs'])
|
2022-06-17 18:23:17 +02:00
|
|
|
|
|
|
|
|
|
// Copy source
|
2022-10-02 13:11:29 -07:00
|
|
|
|
for (const file of ['box.mjs']) {
|
2022-06-17 18:23:17 +02:00
|
|
|
|
cp([...template, 'src', file], [...design, 'src', file])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-18 13:13:08 +00:00
|
|
|
|
function createPlugin(name) {
|
|
|
|
|
const pluginName = 'plugin-' + name
|
|
|
|
|
const template = ['config', 'templates', 'plugin']
|
2023-04-18 13:29:54 +00:00
|
|
|
|
const description = 'A FreeSewing plugin that needs a description'
|
2023-04-18 13:13:08 +00:00
|
|
|
|
const plugin = ['plugins', pluginName]
|
|
|
|
|
const capitalized_name = name.charAt(0).toUpperCase() + name.slice(1)
|
|
|
|
|
|
|
|
|
|
// Create folders
|
|
|
|
|
mkdir([...plugin, 'src'])
|
|
|
|
|
mkdir([...plugin, 'tests'])
|
|
|
|
|
|
|
|
|
|
// Create package.json
|
|
|
|
|
templateOut([...template, 'package.json.mustache'], [...plugin, 'package.json'], {
|
|
|
|
|
pluginName,
|
|
|
|
|
description,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
plugins[pluginName] = description
|
|
|
|
|
write(['config', 'software', 'plugins.json'], JSON.stringify(orderPlugins(plugins), null, 2))
|
|
|
|
|
|
|
|
|
|
// Create index.mjs
|
|
|
|
|
templateOut([...template, 'src', 'index.mjs.mustache'], [...plugin, 'src', 'index.mjs'], {
|
|
|
|
|
name,
|
|
|
|
|
capitalized_name,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-17 18:23:17 +02:00
|
|
|
|
function templateOut(from, to, data) {
|
|
|
|
|
try {
|
|
|
|
|
fs.writeFileSync(
|
|
|
|
|
path.join(process.cwd(), ...to),
|
2022-10-02 13:11:29 -07:00
|
|
|
|
mustache.render(fs.readFileSync(path.join(process.cwd(), ...from), 'utf-8'), data)
|
2022-06-17 18:23:17 +02:00
|
|
|
|
)
|
2022-10-02 13:11:29 -07:00
|
|
|
|
} catch (err) {
|
2022-06-17 18:23:17 +02:00
|
|
|
|
console.log(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function write(to, data) {
|
|
|
|
|
try {
|
|
|
|
|
fs.writeFileSync(path.join(process.cwd(), ...to), data)
|
2022-10-02 13:11:29 -07:00
|
|
|
|
} catch (err) {
|
2022-06-17 18:23:17 +02:00
|
|
|
|
console.log(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mkdir(dir) {
|
|
|
|
|
try {
|
2022-10-02 13:11:29 -07:00
|
|
|
|
fs.mkdirSync(path.join(process.cwd(), ...dir), { recursive: true })
|
|
|
|
|
} catch (err) {
|
2022-06-17 18:23:17 +02:00
|
|
|
|
console.log(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function cp(from, to) {
|
|
|
|
|
try {
|
2022-10-02 13:11:29 -07:00
|
|
|
|
fs.copyFileSync(path.join(process.cwd(), ...from), path.join(process.cwd(), ...to))
|
|
|
|
|
} catch (err) {
|
2022-06-17 18:23:17 +02:00
|
|
|
|
console.log(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function orderDesigns(designs) {
|
|
|
|
|
// Ensure designs are listed alphabetically
|
|
|
|
|
const newDesigns = {}
|
|
|
|
|
for (const type in designs) {
|
|
|
|
|
newDesigns[type] = {}
|
|
|
|
|
for (const design of Object.keys(designs[type]).sort()) {
|
|
|
|
|
newDesigns[type][design] = designs[type][design]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return newDesigns
|
|
|
|
|
}
|
2023-04-18 13:13:08 +00:00
|
|
|
|
function orderPlugins(plugins) {
|
|
|
|
|
// Ensure plugins are listed alphabetically
|
|
|
|
|
const newPlugins = {}
|
|
|
|
|
for (const plugin of Object.keys(plugins).sort()) {
|
|
|
|
|
newPlugins[plugin] = plugins[plugin]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return newPlugins
|
|
|
|
|
}
|