Merge pull request #159 from waku-org/weboko/create-waku-app

feat: add @waku/create-app
This commit is contained in:
Sasha 2022-12-13 10:49:53 +01:00 committed by GitHub
commit 644e8ff703
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 253 additions and 0 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
dist
node_modules
yarn.lock
package-lock.json

2
packages/create-waku-app/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
examples
package-lock.json

View File

@ -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.

View File

@ -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();

View File

@ -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 };

View File

@ -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);

View File

@ -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"
}
}