1
0
Fork 0
freesewing/scripts/add-software.mjs

352 lines
9.9 KiB
JavaScript
Raw Normal View History

import fs from 'fs'
import path from 'path'
import prompts from 'prompts'
chore: Re-structure workspaces, enforce build order These are some changes in the way the monorepo is structured, that are aimed at making it easier to get started. There are two important changes: **Multiple workspaces** We had a yarn workspaces setup at `packages/*`. But our monorepo has grown to 92 packages which can be overwhelming for people not familiar with the package names. To remedy this, I've split it into 4 different workspaces: - `designs/*`: Holds FreeSewing designs (think patterns) - `plugins/*`: Holds FreeSewing plugins - `packages/*`: Holds other packages published on NPM - `sites/*`: Holds software that is not published as an NPM package, such as our various websites and backend API This should make it easier to find things, and to answer questions like *where do I find the code for the plugins*. **Updated reconfigure script to handle build order** One problem when bootstrapping the repo is inter-dependencies between packages. For example, building a pattern will only work once `plugin-bundle` is built. Which will only work once all plugins in the bundle or built. And that will only work when `core` is built, and so on. This can be frustrating for new users as `yarn buildall` will fail. And it gets overlooked by seasoned devs because they're likely to have every package built in their repo so this issue doesn't concern them. To remedy this, we now have a `config/build-order.mjs` file and the updated `/scripts/reconfigure.mjs` script will enforce the build order so that things *just work*.
2022-06-16 17:11:31 +02:00
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' }
chore: Re-structure workspaces, enforce build order These are some changes in the way the monorepo is structured, that are aimed at making it easier to get started. There are two important changes: **Multiple workspaces** We had a yarn workspaces setup at `packages/*`. But our monorepo has grown to 92 packages which can be overwhelming for people not familiar with the package names. To remedy this, I've split it into 4 different workspaces: - `designs/*`: Holds FreeSewing designs (think patterns) - `plugins/*`: Holds FreeSewing plugins - `packages/*`: Holds other packages published on NPM - `sites/*`: Holds software that is not published as an NPM package, such as our various websites and backend API This should make it easier to find things, and to answer questions like *where do I find the code for the plugins*. **Updated reconfigure script to handle build order** One problem when bootstrapping the repo is inter-dependencies between packages. For example, building a pattern will only work once `plugin-bundle` is built. Which will only work once all plugins in the bundle or built. And that will only work when `core` is built, and so on. This can be frustrating for new users as `yarn buildall` will fail. And it gets overlooked by seasoned devs because they're likely to have every package built in their repo so this issue doesn't concern them. To remedy this, we now have a `config/build-order.mjs` file and the updated `/scripts/reconfigure.mjs` script will enforce the build order so that things *just work*.
2022-06-16 17:11:31 +02:00
// Software
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' }
chore: Re-structure workspaces, enforce build order These are some changes in the way the monorepo is structured, that are aimed at making it easier to get started. There are two important changes: **Multiple workspaces** We had a yarn workspaces setup at `packages/*`. But our monorepo has grown to 92 packages which can be overwhelming for people not familiar with the package names. To remedy this, I've split it into 4 different workspaces: - `designs/*`: Holds FreeSewing designs (think patterns) - `plugins/*`: Holds FreeSewing plugins - `packages/*`: Holds other packages published on NPM - `sites/*`: Holds software that is not published as an NPM package, such as our various websites and backend API This should make it easier to find things, and to answer questions like *where do I find the code for the plugins*. **Updated reconfigure script to handle build order** One problem when bootstrapping the repo is inter-dependencies between packages. For example, building a pattern will only work once `plugin-bundle` is built. Which will only work once all plugins in the bundle or built. And that will only work when `core` is built, and so on. This can be frustrating for new users as `yarn buildall` will fail. And it gets overlooked by seasoned devs because they're likely to have every package built in their repo so this issue doesn't concern them. To remedy this, we now have a `config/build-order.mjs` file and the updated `/scripts/reconfigure.mjs` script will enforce the build order so that things *just work*.
2022-06-16 17:11:31 +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(`
Usage:
${chalk.bold.blue('npm run new design')} 👉 Adds a new design
${chalk.bold.blue('npm run new plugin')} 👉 Adds a new plugin
${chalk.bold.blue('npm run 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:
2022-10-02 13:11:29 -07:00
- a firstname like ${chalk.green('alex')}, ${chalk.green('jordan')}, ${chalk.green(
'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
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)',
2023-04-18 13:20:40 +00:00
validate: validateDesignName,
})
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')
console.log(` Installing & linking dependencies...`)
execSync('npm install')
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
👉 We've added ${chalk.green('designs/' + name)} to the local repository
${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(
'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(
'config/dependencies.yaml'
)}
If you change any of these, run ${chalk.blue('npm run reconfigure')} to update the package(s).
${chalk.bold.yellow('👷 Get to work')}
${chalk.gray('≡≡≡≡≡≡≡≡≡≡≡≡≡≡')}
🚀 You can now start the org development environment with ${chalk.blue('npm run org')}
📖 Documentation is available at ${chalk.green('https://freesewing.dev/')}
🤓 Happy hacking
`)
2022-10-02 13:11:29 -07:00
} catch (err) {
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('≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡')}
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')}.
`)
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(
'config/exceptions.yaml'
)}
2023-04-18 13:13:08 +00:00
👉 ${chalk.yellow('Description')}: We used a placeholder description; Update it in ${chalk.green(
'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(
'config/dependencies.yaml'
)}
2023-04-18 13:13:08 +00:00
If you change any of these, run ${chalk.blue('npm run reconfigure')} to update the package(s).
2023-04-18 13:13:08 +00:00
${chalk.bold.yellow('👷 Get to work')}
${chalk.gray('≡≡≡≡≡≡≡≡≡≡≡≡≡≡')}
🛠 You can now start the org development environment with ${chalk.blue('npm run org')}
2023-04-18 13:13:08 +00:00
📖 Documentation is available at ${chalk.green('https://freesewing.dev/')}
🤓 Happy hacking
`)
} catch (err) {
console.log(err)
}
}
}
2023-04-18 13:20:40 +00:00
function validateDesignName(name) {
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`
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. 🤷'
}
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 🤷'
}
function createDesign(name) {
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)
// Add to designs config file
designs[name] = {
2022-10-02 13:11:29 -07:00
code: 'Coder name',
description: description,
2022-10-02 13:11:29 -07:00
design: 'Designer name',
difficulty: 1,
lab: true,
org: true,
2022-10-02 13:11:29 -07:00
tags: ['tagname'],
techniques: ['techname'],
2022-10-02 13:11:29 -07:00
}
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'], {
2022-10-02 13:11:29 -07:00
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
2022-10-02 13:11:29 -07:00
cp([...template, 'tests', 'shared.test.mjs'], [...design, 'tests', 'shared.test.mjs'])
// Copy source
2022-10-02 13:11:29 -07:00
for (const file of ['box.mjs']) {
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,
})
}
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-10-02 13:11:29 -07:00
} catch (err) {
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) {
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) {
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) {
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
}