1
0
Fork 0

wip(new-design): Work on v3 for this package

This commit is contained in:
joostdecock 2023-09-29 09:47:54 +02:00
parent 3604cb918d
commit 3553c43633
40 changed files with 364 additions and 1753 deletions

View file

@ -24,7 +24,7 @@ const nl = '\n'
const tab = ' '
const nlt = nl + tab
// Checks for node 16 or higher
// Checks for node 18 or higher
export const checkNodeVersion = () => {
const node_version = process.version.slice(1).split('.')[0]
if (parseInt(node_version) < config.node) {
@ -47,70 +47,24 @@ export const checkNodeVersion = () => {
}
}
// Helper method to validate the design name
const validateDesignName = (name) => {
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. 🤷'
}
// Gets user input to figure out what to do
export const getChoices = async () => {
const { template } = await prompts({
type: 'select',
name: 'template',
message: 'What template would you like to use? 📑',
choices: [
{ title: 'Tutorial', value: 'tutorial', description: 'Setup the pattern design tutorial' },
{ title: 'From Scratch', value: 'scratch', description: 'Create a design from scratch' },
{
title: 'Extend Brian',
value: 'brian',
description: 'Extend the Brian design (basic torso block for menswear)',
},
{
title: 'Extend Bent',
value: 'bent',
description: 'Extend the Bent design (like brian with added two-part sleeve)',
},
{
title: 'Extend Bella',
value: 'bella',
description: 'Extend the Bella design (womenswear torso block)',
},
{
title: 'Extend Breanna',
value: 'breanna',
description: 'Extend the Breanna design (womenswear torso block - YMMV)',
},
{
title: 'Extend Titan',
value: 'titan',
description: 'Extend the Titan design (gender-neutral trouser block)',
},
],
initial: 0,
})
let finalName = false // we're going to use this to track whether we stay in the naming loop
let overwrite = true // should we overwrite existing files?
const cwd = process.cwd()
let name // name will go here
let sideStep // allows to do something custom
const cwd = process.cwd()
// while we're not finalized on a name
while (finalName === false) {
// request a name
name =
template === 'tutorial' && name === undefined
? 'tutorial'
: (
await prompts({
type: 'text',
name: 'name',
message: 'What name would you like the design to have? 🏷️ ([a-z0-9_] only)',
validate: validateDesignName,
})
).name
name = (
await prompts({
type: 'text',
name: 'name',
message: 'Give a folder name in which we can setup the development environment? 🏷️ ',
})
).name
// check whether a folder with that name already exists
const dest = join(cwd, name)
@ -127,19 +81,28 @@ export const getChoices = async () => {
const { nextStep } = await prompts({
type: 'select',
name: 'nextStep',
message:
'It looks like you already have a design by that name in progress. What should we do?',
message: 'It looks like that folder already exists. What should we do?',
choices: [
{ title: 'Rename', value: 'rename', description: 'Choose a new name for this design' },
{ title: 'Overwrite', value: 'overwrite', description: 'Overwrite the existing design' },
{ title: 'Go back', value: 'rename', description: 'Choose a different folder name' },
{
title: 'Overwrite',
value: 'overwrite',
description: 'Overwrite the contents in the existing folder',
},
{
title: 'Re-initialize',
value: 'reinit',
description:
"Bring in a fresh workbench, but don't overwrite existing design files (useful for updating to the latest dev environment)",
'Re-install depenencies, and update the development environment in this folder',
},
{
title: 'Re-download',
value: 'redownload',
description: 'Update the development environment in this folder',
},
],
})
sideStep = nextStep
// if they said rename, we loop again. otherwise
if (nextStep !== 'rename') {
@ -160,7 +123,7 @@ export const getChoices = async () => {
initial: 0,
})
return { template, name, manager, overwrite }
return { name, manager, overwrite, sideStep }
}
const capitalize = (string) => string.charAt(0).toUpperCase() + string.slice(1)
@ -209,111 +172,6 @@ const copyFileOrTemplate = async (
}
}
// Template the package.json
const copyPackageJson = async (config, choices) => {
const packageJsonTemplate = await readFile(
config.relativeFiles.templates['package.json'],
'utf-8'
)
await copyFileOrTemplate(packageJsonTemplate, config.dest, 'package.json', {
name: choices.name,
tag: config.tag,
dependencies: config.templateData.dependencies,
includeTests: choices.includeTests,
})
}
// Template the design index file
const copyIndexFile = async (config, choices) => {
// Template the index file
const indexTemplate = await readFile(config.relativeFiles.templates['index'], 'utf-8')
// get the part names based on how they are given in the configuration
const partNames = config.complexParts
? config.templateData.parts.map((p) => p.part)
: config.templateData.parts
// write the file
await copyFileOrTemplate(
indexTemplate,
config.dest,
`${designSrcDir}/index.mjs`,
{
name: choices.name,
Name: capitalize(choices.name),
parts: partNames,
},
choices.overwrite
)
}
// Template the part files
const copyPartFiles = async (config, choices) => {
// Template the parts
const partTemplate = await readFile(config.relativeFiles.templates.part, 'utf-8')
// does this design inherit from another?
const doesInherit = !config.templateData.noInheritance
// all part templates need these arguments
const baseConfig = {
name: choices.name, // the name of the design
doesInherit, // whether it's an inherited design
draftUses: {}, // what parameters need to be uncommented in the draft method (default none because part is always uncommented)
}
// if it inherits, we also need the name of the design it inherits from
if (doesInherit) {
baseConfig.baseName = choices.template
baseConfig.BaseName = capitalize(choices.template)
}
// for each part
return config.templateData.parts.map((p) => {
// set up the arguments based on what's in the part's config
const templateArgs = config.complexParts
? {
...baseConfig,
...p,
}
: {
...baseConfig,
part: p,
}
// add an uppercase version of the partName
templateArgs.Part = capitalize(templateArgs.part)
// write the part file
return copyFileOrTemplate(
partTemplate,
config.dest,
`${designSrcDir}/${templateArgs.part}.mjs`,
templateArgs,
choices.overwrite
)
})
}
// Helper method to copy template files
const copyAll = async (config, choices) => {
let promises = []
// Copy shared files
promises = promises.concat(
config.relativeFiles.shared.map((from) => {
if (choices.includeTests || !from.match(/e2e|playwright/))
copyFileOrTemplate(config.source.shared, config.dest, from)
})
)
// template design files
promises.push(copyPackageJson(config, choices))
promises.push(copyIndexFile(config, choices))
promises = promises.concat(copyPartFiles(config, choices))
await Promise.all(promises)
}
// Helper method to run [yarn|npm] install
const installDependencies = async (config, choices) =>
await execa(`${choices.manager} install`, {
@ -322,22 +180,28 @@ const installDependencies = async (config, choices) =>
})
// Helper method to download web environment
const downloadLabFiles = async (config) => {
const downloadFiles = async (config) => {
const promises = []
for (const dir in config.fetch) {
promises.push(
...config.fetch[dir].map(async (file) => {
const to = typeof file === 'string' ? join(config.dest, file) : join(config.dest, file.to)
const to =
typeof file === 'string'
? join(config.dest, file.slice(0, 4) === 'sde/' ? file.slice(4) : file)
: join(config.dest, file.to)
await ensureDir(to)
const url = `${config.fileUri}/${config.repo}/${config.branch}/${dir}/${
typeof file === 'string' ? file : file.from
}`
try {
const res = await axios.get(
`${config.fileUri}/${config.repo}/${config.branch}/${dir}/${
typeof file === 'string' ? file : file.from
}`
const res = await axios.get(url)
await writeFile(
to,
typeof res.data === 'object' ? JSON.stringify(res.data, null, 2) : res.data
)
await writeFile(to, res.data)
} catch (err) {
console.log(err)
if (err.response?.status === 404) console.log(`404: ${url}`)
else console.log(err)
}
})
)
@ -351,7 +215,7 @@ const initGitRepo = async (config, choices) => {
await copyFileOrTemplate(config.gitignore, config.dest, '.gitignore', {}, choices.overwrite)
return execa(
`git init -b main && git add . && git commit -m ":tada: Initialized ${choices.name} repository"`,
`git init -b main && git add . && git commit -m ":tada: Initialized FreeSewing stand-alone development environment"`,
{
cwd: config.dest,
shell: true,
@ -360,13 +224,13 @@ const initGitRepo = async (config, choices) => {
}
// Tips
const showTips = (config, choices) => {
const showTips = (config, choices) =>
console.log(`
All done 🤓 Your new design ${chalk.yellow.bold(
choices.name
)} was initialized in: ${chalk.green.bold(config.dest)}
All done 🤓 Your FreeSewing development environment was initialized in: ${chalk.green.bold(
config.dest
)}
The code for your design is in the ${chalk.yellow.bold('design')} folder.
The templates for various designs are in the ${chalk.yellow.bold('design')} folder.
The other files and folders are the development environment. You can safely ignore those.
To start your development environment, follow these three steps:
@ -377,41 +241,12 @@ const showTips = (config, choices) => {
)}
3) Now open your browser and navigate to ${chalk.green('http://localhost:8000/')}
${chalk.bold.yellow('🤔 More info & help')}
${chalk.gray('≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡')}`)
if (choices.template === 'tutorial')
console.log(`
Our pattern design tutorial is available at: ${chalk.green(
'https://freesewing.dev/tutorials/pattern-design'
)}
It will walk your through the process step by step.
If you get stuck, reach out to our community on Discord: ${chalk.green(
'https://discord.freesewing.dev/'
)}
The ${chalk.bold('development-help')} channel is a good place to ask questions
Don't be shy to reach out. If something is not clear, that's on us, not on you.
So your feedback really helps us improve our tutorial/documentation.
Thanks for giving FreeSewing a shot. We hope you'll 💜 it.
Thanks for giving FreeSewing a shot. I hope you'll 💜 it.
Have fun 🤓
`)
else
console.log(`
FreeSewing's documentation for developers is available at: ${chalk.green(
'https://freesewing.dev/'
)}
Our community is on Discord: ${chalk.green('https://discord.freesewing.dev/')}
The ${chalk.bold('development-help')} channel is a good place to ask for help if you get stuck
Happy hacking 🤓
`)
}
joost
`)
// Creates the environment based on the user's choices
export const createEnvironment = async (choices) => {
@ -423,87 +258,60 @@ export const createEnvironment = async (choices) => {
shared: join(newDesignDir, `shared`),
}
// Create target directory
await mkdir(config.dest, { recursive: true })
// get the template files in a dictionary
const templates = {}
const templateFiles = await rdir(config.source.templates)
templateFiles.forEach((file) => {
const relativeName = relative(config.source.templates, file).replace(/(\.mjs)*\.mustache/, '')
templates[relativeName] = file
})
config.relativeFiles = {
templates,
shared: (await rdir(config.source.shared)).map((file) => relative(config.source.shared, file)),
}
config.templateData = await import(pathToFileURL(config.source.templateData))
// does this base have parts with a lot of attending config?
config.complexParts = typeof config.templateData.parts[0] === 'object'
// Output a linebreak
console.log()
// Copy/Template files
// Download files from GitHub
try {
await oraPromise(copyAll(config, choices), {
await oraPromise(downloadFiles(config), {
text:
chalk.white.bold('🟨⬜⬜⬜ Copying template files') +
chalk.white.dim(' | Just a moment'),
successText: chalk.white.bold('🟩⬜⬜⬜ Copied template files'),
failText: chalk.white.bold(
'🟥⬜⬜⬜ Failed to copy template files | Development environment will not function'
),
})
} catch (err) {
console.log(err)
}
// Install dependencies
try {
await oraPromise(installDependencies(config, choices), {
text:
chalk.white.bold('🟩🟨⬜⬜ Installing dependencies') +
chalk.white.dim(' | Please wait, this will take a while'),
successText: chalk.white.bold('🟩🟩⬜⬜ Installed dependencies'),
failText: chalk.white.bold(
'🟩🟥⬜⬜ Failed to install dependencies | Development environment will not function'
),
})
} catch (err) {
/* no feedback here */
}
// Fetch web components
try {
await oraPromise(downloadLabFiles(config), {
text:
chalk.white.bold('🟩🟩🟨⬜ Downloading web components') +
chalk.white.bold('🟧⬜⬜ Downloading components from GitHub') +
chalk.white.dim(' | Almost there'),
successText: chalk.white.bold('🟩🟩🟩⬜ Downloaded web components'),
successText: chalk.white.bold('🟩⬜⬜ Downloaded components from GitHub'),
failText: chalk.white.bold(
'🟩🟩🟥⬜ Failed to download web components | Development environment will not function'
'🟥⬜⬜ Failed to download components from GitHub | The development environment will not function'
),
})
} catch (err) {
console.log(err)
/* no feedback here */
}
// Initialize git repository
try {
await oraPromise(initGitRepo(config, choices), {
text:
chalk.white.bold('🟩🟩🟩⬜ Initializing git repository') +
chalk.white.dim(' | You have git, right?'),
successText: chalk.white.bold('🟩🟩🟩🟩 Initialized git repository'),
failText:
chalk.white.bold('🟩🟩🟩🟥 Failed to initialize git repository') +
chalk.white.dim(' | This does not stop you from developing your design'),
})
} catch (err) {
console.log(err)
if (!choices.sideStep) {
// Create target directory
await mkdir(config.dest, { recursive: true })
// Install dependencies
try {
await oraPromise(installDependencies(config, choices), {
text:
chalk.white.bold('🟩🟧⬜ Installing dependencies') +
chalk.white.dim(' | Please wait, this will take a while'),
successText: chalk.white.bold('🟩🟩⬜ Installed dependencies'),
failText: chalk.white.bold(
'🟩🟥⬜ Failed to install dependencies | The development environment will not function'
),
})
} catch (err) {
/* no feedback here */
}
}
if (!choices.sideStep) {
// Initialize git repository
try {
await oraPromise(initGitRepo(config, choices), {
text:
chalk.white.bold('🟩🟩🟧 Initializing git repository') +
chalk.white.dim(' | You have git, right?'),
successText: chalk.white.bold('🟩🟩🟩 Initialized git repository'),
failText:
chalk.white.bold('🟩🟩🟥 Failed to initialize git repository') +
chalk.white.dim(' | This does not stop the development environment from functioning'),
})
} catch (err) {
console.log(err)
}
}
// All done. Show tips