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

252 lines
6.6 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'
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' }
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()
}
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 the category for this design 🏷
`)
const { type } = await prompts({
type: 'select',
name: 'type',
message: 'In what category should we add the design?',
choices: [
{ title: 'Accessories', value: 'accessories', description: 'Hats, bags, plushies, and so on' },
{ title: 'Blocks', value: 'blocks', description: 'Blocks/Slopers to base other designs on' },
{ title: 'Garments', value: 'garments', description: 'For clothes. The most common category' },
{ title: 'Utilities', value: 'utilities', description: 'For utility designs such as our rendertest or legend' },
],
initial: 2,
})
console.log(`
Cool cool cool.
Now 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-z] only)',
validate: validateName,
})
if (name && type) {
console.log("\n"+` Alright, let's add ${chalk.green(name)} under ${chalk.green(type)} 🪄`)
createDesign(name, type)
execSync("npm run reconfigure")
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('pacakge.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 a placeholder description; 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/dependecies.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('🙈 Oh no; You called our bluf!')}
${chalk.gray('≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡')}
Adding plugins is not (yet) implemented 😬
Sorry 🤥
`)
}
function validateName(name) {
if ([
...Object.keys(designs.accessories),
...Object.keys(designs.blocks),
...Object.keys(designs.garments),
...Object.keys(designs.utilities)
].indexOf(name) !== -1) return `Sorry but ${name} 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, type) {
const template = ['config', 'templates', 'design']
const design = ['designs', name]
const description = 'FIXME: A FreeSewing pattern that needs a description'
// Add to designs config file
designs[type][name] = description
write(
['config', 'software', 'designs.json'],
JSON.stringify(orderDesigns(designs), null, 2)
)
// Create folders
mkdir([...design, 'src'])
mkdir([...design, 'tests'])
// Create package.json
templateOut(
[...template, 'package.json'],
[...design, 'package.json'],
{ name, description }
)
// Create config file
templateOut(
['config', 'templates', 'design', 'config.js'],
['designs', name, 'config.js'],
{ name, description }
)
// Create tests file
cp(
[...template, 'tests', 'shared.test.mjs'],
[...design, 'tests', 'shared.test.mjs']
)
// Copy source
for (const file of ['index.js', 'box.js']) {
cp([...template, 'src', file], [...design, 'src', file])
}
}
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
}