Merge pull request #159 from waku-org/weboko/create-waku-app
feat: add @waku/create-app
This commit is contained in:
commit
644e8ff703
|
@ -1,3 +1,4 @@
|
|||
dist
|
||||
node_modules
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
examples
|
||||
package-lock.json
|
|
@ -0,0 +1,12 @@
|
|||
## @waku/create-app
|
||||
|
||||
This package is here to help you bootstrap your next Waku dapp.
|
||||
|
||||
Usage:
|
||||
- `yarn create @waku/app [options] <project-dir>`
|
||||
- `npx @waku/create-app [options] <project-dir>`
|
||||
|
||||
For options you can specify template from which to initialize your app. Template correlates directly to the name of example you can see in this repository.
|
||||
|
||||
#### How to add support for new example:
|
||||
Extend `wakuExamples` property defined in `package.json` in this package with the name of the example and relative path to it where folder of the example should be the same as it's name.
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env node
|
||||
const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
|
||||
const packageJson = require("./package.json");
|
||||
const examplesFolder = path.resolve("./examples");
|
||||
|
||||
async function run() {
|
||||
fs.ensureDirSync(examplesFolder);
|
||||
|
||||
const supportedExamples = Object.entries(packageJson.wakuExamples);
|
||||
|
||||
console.log("Started copying supported Waku examples.");
|
||||
|
||||
const copyPromises = supportedExamples.map(async ([name, relativePath]) => {
|
||||
const resolvedPath = path.resolve(__dirname, relativePath);
|
||||
const destinationPath = path.resolve(examplesFolder, name);
|
||||
|
||||
try {
|
||||
await fs.copy(resolvedPath, destinationPath, { filter: nodeModulesFiler });
|
||||
} catch (error) {
|
||||
console.error(`Failed to copy example ${name} to ${destinationPath} with ${error.message}`);
|
||||
throw Error(error.message);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await Promise.all(copyPromises);
|
||||
console.log("Finished copying examples.");
|
||||
} catch (error) {
|
||||
console.error("Failed to copy examples due to " + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
function nodeModulesFiler(src) {
|
||||
if (src.includes("node_modules")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
run();
|
|
@ -0,0 +1,121 @@
|
|||
const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
const execSync = require("child_process").execSync;
|
||||
|
||||
const { Command } = require("commander");
|
||||
const validateProjectName = require("validate-npm-package-name");
|
||||
|
||||
const DEFAULT_TEMPLATE = "web-chat";
|
||||
const supportedExamplesDir = path.resolve(__dirname, "./examples");
|
||||
|
||||
const init = (name, description, version, supportedExamples) => {
|
||||
let appName;
|
||||
const program = new Command()
|
||||
.name(name)
|
||||
.description(description)
|
||||
.version(version, "-v, --version", "output the version number")
|
||||
.arguments("<project-directory>", "Project directory to initialize Waku app")
|
||||
.action(_appName => {
|
||||
appName = _appName;
|
||||
})
|
||||
.option(
|
||||
"-t, --template <path-to-template>",
|
||||
"specify a template for the created project"
|
||||
)
|
||||
.allowUnknownOption()
|
||||
.parse();
|
||||
|
||||
const options = program.opts();
|
||||
const template = options.template || DEFAULT_TEMPLATE;
|
||||
|
||||
if (!supportedExamples[template]) {
|
||||
const supportedExamplesMessage = Object.keys(supportedExamples).reduce((acc, v) => {
|
||||
acc += `\t${v}\n`;
|
||||
return acc;
|
||||
}, "");
|
||||
|
||||
console.error(`Unknown template: ${template}`);
|
||||
console.error(`We support only following templates:\n${supportedExamplesMessage}`)
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
createApp(appName, template);
|
||||
};
|
||||
|
||||
function createApp(name, template) {
|
||||
const appRoot = path.resolve(name);
|
||||
const appName = path.basename(appRoot);
|
||||
|
||||
const templateDir = path.resolve(supportedExamplesDir, template);
|
||||
|
||||
terminateIfAppExists(appName);
|
||||
terminateIfProjectNameInvalid(appName);
|
||||
|
||||
console.log(`Initializing ${appName} from ${template} template.`);
|
||||
|
||||
fs.ensureDirSync(appName);
|
||||
fs.copySync(templateDir, appRoot);
|
||||
|
||||
runNpmInApp(appRoot);
|
||||
runGitInit(appRoot);
|
||||
}
|
||||
|
||||
function runNpmInApp(root) {
|
||||
const packageJsonPath = path.resolve(root, "package.json");
|
||||
|
||||
if (!fs.existsSync(packageJsonPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Installing npm packages.");
|
||||
try {
|
||||
execSync(`npm install --prefix ${root}`, { stdio: "ignore" });
|
||||
console.log("Successfully installed npm dependencies.");
|
||||
} catch (e) {
|
||||
console.warn("Failed to install npm dependencies", e);
|
||||
}
|
||||
}
|
||||
|
||||
function runGitInit(root) {
|
||||
if (isInGitRepository()) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Initiating git repository.");
|
||||
try {
|
||||
execSync(`git init ${root}`, { stdio: "ignore" });
|
||||
console.log("Successfully initialized git repo.");
|
||||
} catch (e) {
|
||||
console.warn("Git repo not initialized", e);
|
||||
}
|
||||
}
|
||||
|
||||
function isInGitRepository() {
|
||||
try {
|
||||
execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore" });
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function terminateIfProjectNameInvalid(name) {
|
||||
const validationResult = validateProjectName(name);
|
||||
|
||||
if (!validationResult.validForNewPackages) {
|
||||
console.error(`Cannot create a project named ${name} because of npm naming restrictions:\n`);
|
||||
[...(validationResult.errors || []), ...(validationResult.warnings || [])]
|
||||
.forEach(error => console.error(` * ${error}`));
|
||||
console.error("\nPlease choose a different project name.");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function terminateIfAppExists(appRoot) {
|
||||
if (fs.existsSync(appRoot)) {
|
||||
console.error(`Cannot create a project because it already exists by the name: ${appRoot}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { init };
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env node
|
||||
const packageJson = require("./package.json");
|
||||
const semver = require("semver");
|
||||
|
||||
const currentNodeVersion = process.versions.node;
|
||||
const supportedNodeVersion = packageJson.engines.node;
|
||||
|
||||
if (!semver.satisfies(currentNodeVersion, supportedNodeVersion)) {
|
||||
console.error(
|
||||
`You are running Node ${currentNodeVersion}.\n` +
|
||||
`@waku/create-app works only with ${packageJson.engines.node}.\n` +
|
||||
`Please update your version of Node.`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { init } = require("./createApp");
|
||||
|
||||
init(packageJson.name, packageJson.description, packageJson.version, packageJson.wakuExamples);
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"name": "@waku/create-app",
|
||||
"version": "0.1.0",
|
||||
"description": "Helper package to bootstrap Waku app ",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/waku-org/js-waku-examples.git",
|
||||
"directory": "packages/create-app"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/waku-org/js-waku-examples/issues"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"createApp.js",
|
||||
"examples"
|
||||
],
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
"create-waku-app": "./index.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node ./build.js",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"keywords": [
|
||||
"waku",
|
||||
"decentralised",
|
||||
"communication",
|
||||
"web3",
|
||||
"ethereum",
|
||||
"dapps"
|
||||
],
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"wakuExamples": {
|
||||
"eth-pm": "../../eth-pm",
|
||||
"light-js": "../../light-js",
|
||||
"relay-angular-chat": "../../relay-angular-chat",
|
||||
"relay-js": "../../relay-js",
|
||||
"relay-reactjs-chat": "../../relay-reactjs-chat",
|
||||
"rln-js": "../../rln-js",
|
||||
"store-js": "../../store-js",
|
||||
"store-reactjs-chat": "../../store-reactjs-chat",
|
||||
"web-chat": "../../web-chat"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": "^9.4.1",
|
||||
"fs-extra": "^11.1.0",
|
||||
"semver": "^7.3.8",
|
||||
"validate-npm-package-name": "^5.0.0"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue