1
0
Fork 0
freesewing/scripts/add-software.mjs
2023-10-22 11:23:30 +02:00

351 lines
9.9 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import fs from 'fs'
import path from 'path'
import prompts from 'prompts'
import chalk from 'chalk'
import { banner } from './banner.mjs'
import mustache from 'mustache'
import { execSync } from 'child_process'
import languages from '../config/languages.json' assert { type: 'json' }
// Software
import designs from '../config/software/designs.json' assert { type: 'json' }
import plugins from '../config/software/plugins.json' assert { type: 'json' }
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()
} else
console.log(`
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 🎉
Let's start by picking a name. Naming things is hard 😬
We'd appreciate if you pick:
- a firstname like ${chalk.green('alex')}, ${chalk.green('jordan')}, ${chalk.green(
'ezra'
)}, or ${chalk.green('logan')}
- that is an aliteration with the kind of design, like ${chalk.green(
'wahid'
)} for a ${chalk.green('w')}aistcoat
Bonus points for picking a name that embraces diversity 🌈 ✊🏾
`)
const { name } = await prompts({
type: 'text',
name: 'name',
message: 'What name would you like the design to have? ([a-z0-9_] only)',
validate: validateDesignName,
})
if (name) {
console.log('\n' + ` Alright, let's add ${chalk.green(name)} 🪄`)
createDesign(name)
execSync('npm run reconfigure')
console.log(` Installing & linking dependencies...`)
execSync('yarn install')
console.log(` All done 🎉`)
try {
console.log(`
${chalk.bold.yellow('✨ Summary')}
${chalk.gray('≡≡≡≡≡≡≡≡≡≡')}
👉 We've created your design skeleton at ${chalk.green('designs/' + name)}
👉 We've configured the packages via the ${chalk.green('package.json')} file
👉 We've added ${chalk.green('designs/' + name)} to the lab
${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(
'config/exceptions.yaml'
)}
👉 ${chalk.yellow('Description')}: We used placeholder metadata; Update it in ${chalk.green(
'config/software/designs.json'
)}
👉 ${chalk.yellow(
'Dependencies'
)}: If you need additional plugins or patterns to extend, update ${chalk.green(
'config/dependencies.yaml'
)}
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)
}
}
}
async function addPlugin() {
console.log(`
${chalk.bold.yellow('👕 Add a new plugin')}
${chalk.gray('≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡')}
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')}.
`)
const { name } = await prompts({
type: 'text',
name: 'name',
message: 'What name would you like the plugin to have? ([a-z] only)',
validate: validatePluginName,
})
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)}
👉 We've configured the packages via the ${chalk.green('package.json')} file
${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(
'config/exceptions.yaml'
)}
👉 ${chalk.yellow('Description')}: We used a placeholder description; Update it in ${chalk.green(
'config/software/plugins.json'
)}
👉 ${chalk.yellow(
'Dependencies'
)}: If you need additional plugins or patterns to extend, update ${chalk.green(
'config/dependencies.yaml'
)}
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)
}
}
}
function validateDesignName(name) {
if (Object.keys(designs).indexOf(name) !== -1)
return `Sorry but ${name} is already taken so you'll need to pick something else`
if (/^([a-z][a-z0-9_]*)$/.test(name)) return true
else
return ' 🙈 Please use only lowercase letters, digits, or underscores. Names must start with a lowercase letter. 🤷'
}
function validatePluginName(name) {
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 🤷'
}
function createDesign(name) {
const template = ['config', 'templates', 'design']
const design = ['designs', name]
const description = 'A FreeSewing pattern that needs a description'
const capitalized_name = name.charAt(0).toUpperCase() + name.slice(1)
// Add to designs config file
designs[name] = {
code: 'Coder name',
description: description,
design: 'Designer name',
difficulty: 1,
lab: true,
org: false,
tags: ['tagname'],
techniques: ['techname'],
}
write(['config', 'software', 'designs.json'], JSON.stringify(orderDesigns(designs), null, 2))
// Create folders
mkdir([...design, 'src'])
mkdir([...design, 'i18n'])
mkdir([...design, 'tests'])
// Create package.json
templateOut([...template, 'package.json.mustache'], [...design, 'package.json'], {
name,
description,
})
// Create src/index.mjs
templateOut([...template, 'src', 'index.mjs.mustache'], [...design, 'src', 'index.mjs'], {
capitalized_name,
})
// 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,
})
// Create tests file
cp([...template, 'tests', 'shared.test.mjs'], [...design, 'tests', 'shared.test.mjs'])
// Copy source
for (const file of ['box.mjs']) {
cp([...template, 'src', file], [...design, 'src', file])
}
}
function createPlugin(name) {
const pluginName = 'plugin-' + name
const template = ['config', 'templates', 'plugin']
const description = 'A FreeSewing plugin that needs a description'
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,
})
}
function templateOut(from, to, data) {
try {
fs.writeFileSync(
path.join(process.cwd(), ...to),
mustache.render(fs.readFileSync(path.join(process.cwd(), ...from), 'utf-8'), data)
)
} catch (err) {
console.log(err)
}
return true
}
function write(to, data) {
try {
fs.writeFileSync(path.join(process.cwd(), ...to), data)
} catch (err) {
console.log(err)
}
return true
}
function mkdir(dir) {
try {
fs.mkdirSync(path.join(process.cwd(), ...dir), { recursive: true })
} catch (err) {
console.log(err)
}
return true
}
function cp(from, to) {
try {
fs.copyFileSync(path.join(process.cwd(), ...from), path.join(process.cwd(), ...to))
} catch (err) {
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
}
function orderPlugins(plugins) {
// Ensure plugins are listed alphabetically
const newPlugins = {}
for (const plugin of Object.keys(plugins).sort()) {
newPlugins[plugin] = plugins[plugin]
}
return newPlugins
}