Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: better frameworks like cli #115

Merged
merged 44 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
a78b815
better cli
vasucp1207 Aug 3, 2023
abc2096
playground
vasucp1207 Aug 3, 2023
04cdc5c
build project
vasucp1207 Aug 3, 2023
c81c506
build project
vasucp1207 Aug 3, 2023
78551d2
remove esbuild templates
vasucp1207 Aug 4, 2023
51df8a4
remove esbuild templates
vasucp1207 Aug 4, 2023
1bdaba8
update test
vasucp1207 Aug 4, 2023
1d36e1e
trying building the package
vasucp1207 Aug 5, 2023
c387dcd
trying building the package
vasucp1207 Aug 5, 2023
82cd7ac
move files to scr/ folder
vasucp1207 Aug 6, 2023
5459f1b
move files to scr/ folder
vasucp1207 Aug 6, 2023
0499830
deepMerge fix
vasucp1207 Aug 6, 2023
2884f69
adding file to package.json
vasucp1207 Aug 6, 2023
26cb8db
adding template/ to package
vasucp1207 Aug 7, 2023
a0e0890
build change
vasucp1207 Aug 7, 2023
58b4e46
remove emptyDir and renderDir
vasucp1207 Aug 8, 2023
91da4bf
replace with cross-spawn
vasucp1207 Aug 9, 2023
ac5536a
replace with cross-spawn
vasucp1207 Aug 9, 2023
c375ed7
remove cross-spawn
vasucp1207 Aug 10, 2023
f027c6c
remove cross-spawn
vasucp1207 Aug 10, 2023
e01c429
Update contrib/create-waku/src/cli.ts
vasucp1207 Aug 12, 2023
c8e8b96
Update contrib/create-waku/src/cli.ts
vasucp1207 Aug 12, 2023
5ffbd5e
Update contrib/create-waku/src/cli.ts
vasucp1207 Aug 12, 2023
0713dc4
relative to the script location
vasucp1207 Aug 14, 2023
045d405
remove test file
vasucp1207 Aug 16, 2023
6cffde3
Update contrib/create-waku/src/cli.ts
vasucp1207 Aug 16, 2023
2a98e6a
Update contrib/create-waku/src/cli.ts
vasucp1207 Aug 16, 2023
2fc635b
refactor
vasucp1207 Aug 16, 2023
5fa654c
packageJson write
vasucp1207 Aug 16, 2023
00bc21f
packageJson write
vasucp1207 Aug 16, 2023
ef34f61
remove render
vasucp1207 Aug 17, 2023
6b1ede3
remove render
vasucp1207 Aug 17, 2023
c65f424
rebase
vasucp1207 Aug 30, 2023
9dff783
new monorepo structure
vasucp1207 Aug 30, 2023
b7f030c
new monorepo structure
vasucp1207 Aug 30, 2023
f025623
update dir structure
vasucp1207 Aug 30, 2023
6b990b3
Update package.json
vasucp1207 Aug 30, 2023
472d830
update pnpm yaml
vasucp1207 Sep 2, 2023
31792f0
Update packages/create-waku/package.json
dai-shi Sep 3, 2023
2a52aac
types for fs-extra
vasucp1207 Sep 3, 2023
81a54d0
Update types.d.ts
vasucp1207 Sep 3, 2023
016f005
fix prettier issue
vasucp1207 Sep 4, 2023
47e71fa
Update .gitignore
vasucp1207 Sep 5, 2023
4c549d9
Update packages/create-waku/src/cli.ts
dai-shi Sep 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 141 additions & 39 deletions contrib/create-waku/cli.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,148 @@
#!/usr/bin/env node

const path = require("node:path");
const fs = require("node:fs");
const https = require("node:https");
import fs from 'node:fs'
import path from 'node:path'
import prompts from 'prompts'
import { red, green, bold } from 'kolorist'

const dirName = "waku-example";
import emptyDir from './emptyDir.js'
import renderTemplate from './renderTemplate.js'

if (fs.existsSync(dirName)) {
console.error(`Directory "${dirName}" already exists!`);
process.exit(1);
function isValidPackageName(projectName) {
return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(
projectName
)
}

const baseUrl =
"https://raw.githubusercontent.com/dai-shi/waku/v0.14.0/examples/01_counter/";

const files = `
package.json
tsconfig.json
src/main.tsx
src/entries.ts
src/index.html
src/components/App.tsx
src/components/Counter.tsx
`
.split(/\s/)
.filter((file) => file);

const getFiles = (index = 0) => {
const file = files[index];
if (!file) return;
const destFile = path.join(dirName, file.replace("/", path.sep));
fs.mkdirSync(path.dirname(destFile), { recursive: true });
https.get(baseUrl + file, (res) => {
res.pipe(fs.createWriteStream(destFile));
res.on("end", () => getFiles(index + 1));
});
};

getFiles();

process.on("exit", (code) => {
if (!code) {
console.info(`Done! Change directory "${dirName}"`);
function toValidPackageName(projectName) {
return projectName
.trim()
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/^[._]/, '')
.replace(/[^a-z0-9-~]+/g, '-')
}

// if the dir is empty or not exist
function canSafelyOverwrite(dir) {
return !fs.existsSync(dir) || fs.readdirSync(dir).length === 0
}

async function init() {
const cwd = process.cwd();

let targetDir;
let defaultProjectName = 'waku-project'

const CHOICES = fs.readdirSync(path.resolve(cwd, '../../examples'))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work as we don't publish examples dir.

We have two options.

  1. add build step to copy examples dir
  2. download from github

Eventually, we should go with 1 because it fixes #81, but for now 2 is also fine.
If we were to go with 1, we would like to make the code in TypeScript and transpile with SWC in the build step.

Copy link
Contributor Author

@vasucp1207 vasucp1207 Aug 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work as we don't publish examples dir.

We have two options.

  1. add build step to copy examples dir
  2. download from github

Eventually, we should go with 1 because it fixes #81, but for now 2 is also fine. If we were to go with 1, we would like to make the code in TypeScript and transpile with SWC in the build step.

Using esbuild,

  • If we npm run build:esbuild an out.js file generated.
  • Running npm run build:estemplate generates an copy.js file.
  • If we run node copy.js templates copied from example dir and a template folder generated.
  • Finally, running node cli.js works fine.

Problem with transpiling with swr,

  • If we transpiling files with swr then the imports in output files are not working ex,
import { renderTemplate } from './renderTemplate'

This import statement would not be able to resolve the file.

Is, I'm missing something about this swr.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I don't mind using esbuild for now. I just thought SWC might work better and we also use it in waku. We can try that later.

Can you move TS files into ./src folder please?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, sure we can look around it in the near future.

let result = {}

try {
result = await prompts([
{
name: 'projectName',
type: 'text',
message: 'Project Name',
initial: defaultProjectName,
onState: (state) => (targetDir = String(state.value).trim() || defaultProjectName)
},
{
name: 'shouldOverwrite',
type: () => canSafelyOverwrite(targetDir) ? null : 'confirm',
message: `${targetDir} is not empty. Remove existing files and continue?`,
},
{
name: 'overwriteChecker',
type: (values = {}) => {
if (values.shouldOverwrite === false) {
throw new Error(red('✖') + ' Operation cancelled')
}
return null
}
},
{
name: 'packageName',
type: () => (isValidPackageName(targetDir) ? null : 'text'),
message: 'Package name',
initial: () => toValidPackageName(targetDir),
validate: (dir) => isValidPackageName(dir) || 'Invalid package.json name'
},
{
name: 'chooseProject',
type: 'select',
message: 'Choose a starter template',
choices: [
{ title: 'basic', value: CHOICES[0] },
{ title: 'async-template', value: CHOICES[1] },
{ title: 'promise-template', value: CHOICES[2] },
],
}
], {
onCancel: () => {
throw new Error(red('✖') + ' Operation cancelled')
}
})
} catch (cancelled) {
console.log(cancelled.message)
process.exit(1)
}

const { packageName, shouldOverwrite, chooseProject } = result

const root = path.join(cwd, targetDir)
// const root = path.resolve(cwd, '../playground', targetDir)

if (shouldOverwrite) {
emptyDir(root)
} else if (!fs.existsSync(root)) {
fs.mkdirSync(root)
}

const pkg = { name: packageName, version: '0.0.0' }

fs.writeFileSync(
path.resolve(root, 'package.json'),
JSON.stringify(pkg, null, 2),
)

console.log("Setting up project...")
const templateRoot = path.resolve(cwd, '../../examples')

const render = function render(templateName) {
const templateDir = path.resolve(templateRoot, templateName)
renderTemplate(templateDir, root)
}

render(chooseProject)

const packageManager = /pnpm/.test(process.env.npm_execpath)
? 'pnpm'
: /yarn/.test(process.env.npm_execpath)
? 'yarn'
: 'npm'

const commandsMap = {
install: {
pnpm: 'pnpm install',
yarn: 'yarn',
npm: 'npm install'
},
dev: {
pnpm: 'pnpm dev',
yarn: 'yarn dev',
npm: 'npm run dev'
}
}

console.log(`\nDone. Now run:\n`)
if (root !== cwd) {
console.log(`${bold(green(`cd ${path.relative(cwd, root)}`))}`)
}
});
console.log(`${bold(green(commandsMap.install[packageManager]))}`)
console.log(`${bold(green(commandsMap.dev[packageManager]))}`)
console.log()
}

init().catch((e) => {
console.error(e)
})
dai-shi marked this conversation as resolved.
Show resolved Hide resolved
19 changes: 19 additions & 0 deletions contrib/create-waku/deepMerge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const isObject = val => val && typeof val === 'object'
const mergeArrayWithDedupe = (a, b) => Array.from(new Set([...a, ...b]))

export default function deepMerge(target, obj) {
for (const key of Object.keys(obj)) {
const oldVal = target[key]
const newVal = obj[key]

if (Array.isArray(oldVal) && Array.isArray(newVal)) {
target[key] = mergeArrayWithDedupe(oldVal, newVal)
} else if (isObject(oldVal) && isObject(newVal)) {
target[key] = deepMerge(oldVal, newVal)
} else {
target[key] = newVal
}
}

return target
}
19 changes: 19 additions & 0 deletions contrib/create-waku/emptyDir.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import fs from 'fs';
import path from 'path';

export default function emptyDir(dir) {
// if the file empty
if (!fs.existsSync(dir)) {
return;
}
for (const file of fs.readdirSync(dir)) {
const abs = path.resolve(dir, file);
if (fs.lstatSync(abs).isDirectory()) {
emptyDir(abs);
// after emptying the dir remove the dir itself
fs.rmdirSync(abs);
} else {
fs.unlinkSync(abs);
}
}
}
32 changes: 32 additions & 0 deletions contrib/create-waku/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion contrib/create-waku/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
{
"name": "create-waku",
"version": "0.4.10",
"bin": "./cli.js"
"bin": "./cli.js",
"type": "module",
"scripts": {
dai-shi marked this conversation as resolved.
Show resolved Hide resolved
"test": "node ./tests/generateTemplates.test.js"
},
"dependencies": {
"kolorist": "^1.8.0",
"prompts": "^2.4.2"
}
}
36 changes: 36 additions & 0 deletions contrib/create-waku/renderTemplate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import fs from 'fs';
import path from 'path';
import deepMerge from './deepMerge.js'

function renderTemplate(src, dest) {
const stats = fs.statSync(src);

if (stats.isDirectory()) {
fs.mkdirSync(dest, { recursive: true });
for (const file of fs.readdirSync(src)) {
renderTemplate(path.resolve(src, file), path.resolve(dest, file));
}
return;
}

const filename = path.basename(src);

if (filename === 'package.json' && fs.existsSync(dest)) {
// merge instead of overwriting
const pkg = deepMerge(
JSON.parse(fs.readFileSync(dest)),
JSON.parse(fs.readFileSync(src))
);
fs.writeFileSync(dest, JSON.stringify(pkg, null, 2) + '\n');
return;
}

if (filename.startsWith('_')) {
// rename `_file` to `.file`
dest = path.resolve(path.dirname(dest), filename.replace(/^_/, '.'));
}

fs.copyFileSync(src, dest);
}

export default renderTemplate;
33 changes: 33 additions & 0 deletions contrib/create-waku/tests/generateTemplates.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import fs from 'fs'
import path from 'path'
import assert from 'assert'
import { green, bold } from 'kolorist'

import renderTemplate from '../renderTemplate.js'
import emptyDir from '../emptyDir.js'

function generateTemplate(targetDir, templateDir) {
renderTemplate(templateDir, targetDir)
}

// Test case
function testTemplateGeneration() {
const cwd = process.cwd()

const templateDir = path.resolve(cwd, '../../examples', '01_counter')
const targetDir = path.resolve(cwd, './playground', 'basic')

// Generate the template
generateTemplate(targetDir, templateDir);

// Check if the file was generated
const fileExists = fs.existsSync(targetDir);
assert.strictEqual(fileExists, true, console.log(bold(green(`Template generated successfully.`))))

// Clean up the generated file after the test
emptyDir(targetDir)
fs.rmdirSync(targetDir);
}

// Run the test
testTemplateGeneration()