├── .eslintrc ├── .github └── ISSUE_TEMPLATE │ └── issue.md ├── .gitignore ├── .prettierignore ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── lib ├── create-package │ ├── applyName.js │ ├── applyPhpNamespace.js │ ├── copyTemplates.js │ ├── createExampleFiles.js │ ├── execute.js │ ├── index.js │ ├── program.js │ └── prompt.js ├── create-plugin │ ├── applyPhpConstants.js │ ├── applyPhpConstantsAndNamespace.js │ ├── applyPhpFunctions.js │ ├── applyPromptsToTemplates.js │ ├── applySlug.js │ ├── copyTemplates.js │ ├── execute.js │ ├── index.js │ ├── installPlugin.js │ ├── program.js │ └── prompt.js ├── create-workspace │ ├── applyPhpUtils.js │ ├── applyPorts.js │ ├── applyWorkspaceName.js │ ├── checkDependencies.js │ ├── completeWorkspace.js │ ├── createDotEnv.js │ ├── createGitFolder.js │ ├── execute.js │ ├── index.js │ ├── program.js │ ├── prompt.js │ ├── promptGitLab.js │ ├── removeExamplePlugin.js │ └── runThirdPartyLicenses.js ├── index.js ├── misc │ ├── applyGitLabCi.js │ ├── applyPackageJson.js │ ├── applyPhpNamespace.js │ ├── index.js │ ├── index.jsx │ ├── modifyRootGitLabCiInclude.js │ ├── newsletterPrompt.js │ └── regenerateLaunchJson.js └── utils.js ├── package.json ├── src ├── create-package │ ├── applyName.ts │ ├── copyTemplates.ts │ ├── createExampleFiles.ts │ ├── execute.ts │ ├── index.ts │ ├── program.ts │ └── prompt.ts ├── create-plugin │ ├── applyPhpConstants.ts │ ├── applyPhpFunctions.ts │ ├── applyPromptsToTemplates.ts │ ├── applySlug.ts │ ├── copyTemplates.ts │ ├── execute.ts │ ├── index.ts │ ├── installPlugin.ts │ ├── program.ts │ └── prompt.ts ├── create-workspace │ ├── applyPorts.ts │ ├── applyWorkspaceName.ts │ ├── checkDependencies.ts │ ├── completeWorkspace.ts │ ├── createDotEnv.ts │ ├── createGitFolder.ts │ ├── execute.ts │ ├── index.ts │ ├── program.ts │ ├── prompt.ts │ ├── promptGitLab.ts │ ├── removeExamplePlugin.ts │ └── runThirdPartyLicenses.ts ├── index.ts ├── misc │ ├── applyGitLabCi.ts │ ├── applyPackageJson.ts │ ├── applyPhpNamespace.ts │ ├── index.ts │ ├── modifyRootGitLabCiInclude.ts │ ├── newsletterPrompt.ts │ └── regenerateLaunchJson.ts └── utils.ts ├── tsconfig.json └── yarn.lock /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "prettier", 9 | "plugin:prettier/recommended", 10 | "plugin:@typescript-eslint/eslint-recommended", 11 | "plugin:@typescript-eslint/recommended" 12 | ], 13 | "parser": "@typescript-eslint/parser", 14 | "parserOptions": { 15 | "ecmaFeatures": { 16 | "jsx": true 17 | }, 18 | "ecmaVersion": 2018, 19 | "sourceType": "module" 20 | }, 21 | "plugins": ["@typescript-eslint"], 22 | "rules": { 23 | "one-var": ["error", "never"] 24 | }, 25 | "overrides": [ 26 | { 27 | "files": ["**/*.{tsx,ts}"], 28 | "rules": { 29 | "@typescript-eslint/no-explicit-any": 0, 30 | "@typescript-eslint/explicit-function-return-type": 0, 31 | "@typescript-eslint/ban-ts-ignore": 0, 32 | "@typescript-eslint/member-ordering": "error" 33 | } 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Issue 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Please report any issues to https://github.com/matzeeable/wp-reactjs-starter/issues 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Example folder with plugins (for development purposes) 2 | my-awesomeness 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (https://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # TypeScript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | # next.js build output 64 | .next 65 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | lib/ -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "dbaeumer.vscode-eslint", 5 | "ms-vscode.vscode-typescript-tslint-plugin", 6 | "steoates.autoimport", 7 | "stringham.move-ts", 8 | "streetsidesoftware.code-spell-checker" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.enable": true, 3 | "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"], 4 | "editor.useTabStops": false, 5 | "editor.formatOnSave": false, 6 | "editor.tabSize": 4, 7 | "files.exclude": { 8 | "**/.git": true, 9 | "**/.svn": true, 10 | "**/.hg": true, 11 | "**/CVS": true, 12 | "**/.DS_Store": true 13 | }, 14 | "[typescript]": { 15 | "editor.formatOnSave": true 16 | }, 17 | "[typescriptreact]": { 18 | "editor.formatOnSave": true 19 | }, 20 | "[javascript]": { 21 | "editor.formatOnSave": true 22 | }, 23 | "[jsonc]": { 24 | "editor.formatOnSave": true 25 | }, 26 | "[markdown]": { 27 | "editor.formatOnSave": true 28 | }, 29 | "[json]": { 30 | "editor.formatOnSave": true 31 | }, 32 | "cSpell.enabled": true 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 devowl.io GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WP React Starter: WordPress React Boilerplate 2 | 3 | WP React Starter Logo 4 | 5 | Create (multiple) WordPress plugins that use **React**, **TypeScript**, and **object-oriented PHP** in a fully customizable **Docker** development environment, commited in a **monorepo**. 6 | 7 | > Wow, I didn't know the WordPress plugin development could look like this! 8 | 9 | 🚀 **Instant no-config plugin creation with** [**create-wp-react-app**](https://github.com/devowlio/create-wp-react-app) 🔥 10 | 11 | [![GitHub stars](https://img.shields.io/github/stars/devowlio/wp-react-starter?style=flat&logo=github)](https://github.com/devowlio/wp-react-starter) 12 | [![Join on Slack](https://img.shields.io/badge/Slack-join-green.svg?style=flat&logo=slack)](https://matthias-web.com/slack) 13 | [![codecov](https://codecov.io/gl/devowlio/wp-reactjs-starter/branch/master/graph/badge.svg)](https://codecov.io/gl/devowlio/wp-reactjs-starter) 14 | [![GitLab CI/CD](https://img.shields.io/badge/CI%20%2F%20CD-See%20history-green?logo=gitlab)](https://gitlab.com/devowlio/wp-reactjs-starter/pipelines) 15 | 16 | --- 17 | 18 | This CLI tool enables you to create a modern WordPress React plugin with no build configuration based on [WP React Starter](https://github.com/devowlio/wp-react-starter). 19 | 20 | **Please read documentation of [devowlio/wp-react-starter](https://github.com/devowlio/wp-react-starter) as this repository is only the command line tool.** 21 | -------------------------------------------------------------------------------- /lib/create-package/applyName.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __spreadArrays = (this && this.__spreadArrays) || function () { 3 | for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; 4 | for (var r = Array(s), k = 0, i = 0; i < il; i++) 5 | for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) 6 | r[k] = a[j]; 7 | return r; 8 | }; 9 | var __importDefault = (this && this.__importDefault) || function (mod) { 10 | return (mod && mod.__esModule) ? mod : { "default": mod }; 11 | }; 12 | Object.defineProperty(exports, "__esModule", { value: true }); 13 | var chalk_1 = __importDefault(require("chalk")); 14 | var fs_extra_1 = require("fs-extra"); 15 | var path_1 = require("path"); 16 | var utils_1 = require("../utils"); 17 | var glob_1 = __importDefault(require("glob")); 18 | /** 19 | * Apply a package name to a given cwd. The scope name is automatically read from 20 | * the parent root package.json#name variable. 21 | * 22 | * @param createPackageCwd 23 | * @param name 24 | */ 25 | function applyName(createPackageCwd, name) { 26 | // Generate scoped package name 27 | var scopeName = JSON.parse(fs_extra_1.readFileSync(path_1.resolve(createPackageCwd, "../../package.json"), { encoding: utils_1.DEFAULT_ENCODING })).name + 28 | "/" + 29 | name; 30 | utils_1.logProgress("Apply package name " + chalk_1.default.underline(scopeName) + " to " + chalk_1.default.underline(createPackageCwd)); 31 | var globFiles = function (pattern) { return glob_1.default.sync(pattern, { cwd: createPackageCwd, absolute: true }); }; 32 | var files = __spreadArrays(globFiles("{package,composer}.json"), globFiles("README.md")); 33 | utils_1.searchAndReplace(files, /wp-reactjs-multi-starter\/utils/g, scopeName); 34 | utils_1.logSuccess("Successfully applied name to package!"); 35 | return scopeName; 36 | } 37 | exports.applyName = applyName; 38 | -------------------------------------------------------------------------------- /lib/create-package/applyPhpNamespace.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __spreadArrays = (this && this.__spreadArrays) || function () { 3 | for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; 4 | for (var r = Array(s), k = 0, i = 0; i < il; i++) 5 | for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) 6 | r[k] = a[j]; 7 | return r; 8 | }; 9 | var __importDefault = (this && this.__importDefault) || function (mod) { 10 | return (mod && mod.__esModule) ? mod : { "default": mod }; 11 | }; 12 | Object.defineProperty(exports, "__esModule", { value: true }); 13 | var utils_1 = require("../utils"); 14 | var chalk_1 = __importDefault(require("chalk")); 15 | var glob_1 = __importDefault(require("glob")); 16 | /** 17 | * Apply PHP namespace to package source files. 18 | * 19 | * @param createPackageCwd 20 | * @param namespace 21 | */ 22 | function applyPhpNamespace(createPackageCwd, namespace) { 23 | // Apply the namespace 24 | utils_1.logProgress("Apply namespace " + chalk_1.default.underline(namespace) + " to all PHP files for autoloading..."); 25 | var globFiles = function (pattern) { return glob_1.default.sync(pattern, { cwd: createPackageCwd, absolute: true }); }; 26 | utils_1.searchAndReplace(globFiles("composer.*"), /MatthiasWeb\\\\Utils/g, namespace.replace(/\\/g, "\\\\")); 27 | utils_1.searchAndReplace(__spreadArrays(globFiles("src/**/*.php"), globFiles("test/phpunit/**/*.php")), /MatthiasWeb\\Utils/g, namespace); 28 | utils_1.logSuccess("Successfully applied PHP namespace to package!"); 29 | } 30 | exports.applyPhpNamespace = applyPhpNamespace; 31 | -------------------------------------------------------------------------------- /lib/create-package/copyTemplates.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var rimraf_1 = __importDefault(require("rimraf")); 7 | var chalk_1 = __importDefault(require("chalk")); 8 | var path_1 = require("path"); 9 | var utils_1 = require("../utils"); 10 | var fs_extra_1 = require("fs-extra"); 11 | /** 12 | * Copy templates to the given path and read template files. 13 | * 14 | * @param createPackageCwd 15 | */ 16 | function copyTemplates(createPackageCwd) { 17 | utils_1.logProgress("Create new package from template..."); 18 | var templateCwd = path_1.resolve(createPackageCwd, "../..", utils_1.FOLDER_CWRA, "template-package"); 19 | fs_extra_1.copySync(templateCwd, createPackageCwd); 20 | // Delete language folder 21 | rimraf_1.default.sync(path_1.resolve(createPackageCwd, "languages")); 22 | utils_1.logSuccess("Successfully created package folder " + chalk_1.default.underline(createPackageCwd)); 23 | } 24 | exports.copyTemplates = copyTemplates; 25 | -------------------------------------------------------------------------------- /lib/create-package/createExampleFiles.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var chalk_1 = __importDefault(require("chalk")); 7 | var utils_1 = require("../utils"); 8 | var fs_extra_1 = require("fs-extra"); 9 | var path_1 = require("path"); 10 | /** 11 | * Create an example MyClass.php file and an example .tsx file. 12 | * 13 | * @param createPackageCwd 14 | * @param packageName 15 | * @param namespace 16 | */ 17 | function createExampleFiles(createPackageCwd, packageName, namespace) { 18 | utils_1.logProgress("Create example PHP and TSX file in " + chalk_1.default.underline(createPackageCwd) + "..."); 19 | var indexTsx = "import \"setimmediate\"; // Polyfill for yielding\n\nfunction sayHello() {\n console.log(\"Hello from " + packageName + "\");\n}\n\nexport { sayHello };\n"; 20 | var myClassPhp = " 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | var __importDefault = (this && this.__importDefault) || function (mod) { 39 | return (mod && mod.__esModule) ? mod : { "default": mod }; 40 | }; 41 | Object.defineProperty(exports, "__esModule", { value: true }); 42 | var _1 = require("./"); 43 | var path_1 = require("path"); 44 | var fs_extra_1 = require("fs-extra"); 45 | var utils_1 = require("../utils"); 46 | var chalk_1 = __importDefault(require("chalk")); 47 | var execa_1 = __importDefault(require("execa")); 48 | var misc_1 = require("../misc"); 49 | var applyName_1 = require("./applyName"); 50 | /** 51 | * Generate a new package from the template. All validations are done in createPackagePrompt. 52 | * 53 | * @param root 54 | * @param input 55 | * @param fromWorkspace 56 | * @returns 57 | * @throws 58 | */ 59 | function createPackageExecute(root, input) { 60 | return __awaiter(this, void 0, void 0, function () { 61 | var createPackageCwd; 62 | return __generator(this, function (_a) { 63 | createPackageCwd = path_1.resolve(input.cwd, "packages", input.packageName); 64 | // Strictly do not override an existing plugin!! 65 | if (fs_extra_1.existsSync(createPackageCwd)) { 66 | throw new Error("You already have a package with name " + input.packageName + "."); 67 | } 68 | _1.copyTemplates(createPackageCwd); 69 | applyName_1.applyName(createPackageCwd, input.packageName); 70 | misc_1.applyPhpNamespace(createPackageCwd, input.namespace, "utils"); 71 | misc_1.applyGitLabCi(createPackageCwd, input.abbreviation, "utils "); 72 | misc_1.applyGitLabCi(createPackageCwd, input.packageName, "utils"); 73 | misc_1.applyPackageJson(root, createPackageCwd, { 74 | author: input.author, 75 | description: input.packageDesc, 76 | homepage: input.packageUri, 77 | version: "1.0.0" 78 | }, false); 79 | misc_1.modifyRootGitLabCiInclude("add", input.cwd, input.packageName, "packages"); 80 | utils_1.generateLicenseFile(createPackageCwd, input.author, input.packageDesc); 81 | _1.createExampleFiles(createPackageCwd, input.packageName, input.namespace); 82 | utils_1.logSuccess("Successfully created package " + input.packageName + " in " + chalk_1.default.underline(createPackageCwd)); 83 | utils_1.logProgress("Bootstrap and link new package..."); 84 | execa_1.default.sync("yarn", ["bootstrap"], { cwd: input.cwd, stdio: "inherit" }); 85 | execa_1.default.sync("yarn", ["lerna", "link"], { cwd: input.cwd, stdio: "inherit" }); 86 | utils_1.runThirdPartyLicenseForPackage(createPackageCwd); 87 | misc_1.regenerateLaunchJson(input.cwd); 88 | utils_1.logProgress("Rebuild the development environment, afterwards you can use your new package..."); 89 | execa_1.default("yarn", ["docker:start"], { cwd: input.cwd, stdio: "inherit" }); 90 | return [2 /*return*/]; 91 | }); 92 | }); 93 | } 94 | exports.createPackageExecute = createPackageExecute; 95 | -------------------------------------------------------------------------------- /lib/create-package/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function __export(m) { 3 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; 4 | } 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | __export(require("./program")); 7 | __export(require("./applyName")); 8 | __export(require("./prompt")); 9 | __export(require("./execute")); 10 | __export(require("./copyTemplates")); 11 | __export(require("./createExampleFiles")); 12 | -------------------------------------------------------------------------------- /lib/create-package/program.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var commander_1 = require("commander"); 4 | var prompt_1 = require("./prompt"); 5 | var createPackageCommand = commander_1.command("create-package") 6 | .description("Creates a new package in packages/.") 7 | .option("--cwd ", "The path to the workspace where the package should be created", process.cwd()) 8 | .option("--package-name ", "How should your package be named?") 9 | .option("--package-desc ", "Give a one-line package description?") 10 | .option("--author ", "Who is the package author?") 11 | .option("--package-uri ", "What's your package' homepage URL?") 12 | .option("--namespace ", "What's the PHP Namespace (e. g 'MatthiasWeb\\WPRJSS')?") 13 | .option("--abbreviation ", "What's a short abbreviation for this package?") 14 | .action(function (args) { return prompt_1.createPackagePrompt(args); }); 15 | exports.createPackageCommand = createPackageCommand; 16 | -------------------------------------------------------------------------------- /lib/create-package/prompt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __assign = (this && this.__assign) || function () { 3 | __assign = Object.assign || function(t) { 4 | for (var s, i = 1, n = arguments.length; i < n; i++) { 5 | s = arguments[i]; 6 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) 7 | t[p] = s[p]; 8 | } 9 | return t; 10 | }; 11 | return __assign.apply(this, arguments); 12 | }; 13 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 14 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 15 | return new (P || (P = Promise))(function (resolve, reject) { 16 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 17 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 18 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 19 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 20 | }); 21 | }; 22 | var __generator = (this && this.__generator) || function (thisArg, body) { 23 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 24 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 25 | function verb(n) { return function (v) { return step([n, v]); }; } 26 | function step(op) { 27 | if (f) throw new TypeError("Generator is already executing."); 28 | while (_) try { 29 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 30 | if (y = 0, t) op = [op[0] & 2, t.value]; 31 | switch (op[0]) { 32 | case 0: case 1: t = op; break; 33 | case 4: _.label++; return { value: op[1], done: false }; 34 | case 5: _.label++; y = op[1]; op = [0]; continue; 35 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 36 | default: 37 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 38 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 39 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 40 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 41 | if (t[2]) _.ops.pop(); 42 | _.trys.pop(); continue; 43 | } 44 | op = body.call(thisArg, _); 45 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 46 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 47 | } 48 | }; 49 | var __importDefault = (this && this.__importDefault) || function (mod) { 50 | return (mod && mod.__esModule) ? mod : { "default": mod }; 51 | }; 52 | Object.defineProperty(exports, "__esModule", { value: true }); 53 | var inquirer_1 = require("inquirer"); 54 | var _1 = require("./"); 55 | var utils_1 = require("../utils"); 56 | var path_1 = require("path"); 57 | var slugify_1 = __importDefault(require("slugify")); 58 | var misc_1 = require("../misc"); 59 | /** 60 | * Prompt for CLI arguments which are not passed. 61 | * 62 | * @param opts 63 | */ 64 | function createPackagePrompt(_a) { 65 | var cwd = _a.cwd, packageName = _a.packageName, packageDesc = _a.packageDesc, author = _a.author, packageUri = _a.packageUri, namespace = _a.namespace, abbreviation = _a.abbreviation; 66 | return __awaiter(this, void 0, void 0, function () { 67 | var createWorkspaceCwd, root, gitName, mockData, answers, _b, parsed, e_1; 68 | return __generator(this, function (_c) { 69 | switch (_c.label) { 70 | case 0: 71 | createWorkspaceCwd = cwd ? path_1.resolve(cwd) : process.cwd(); 72 | root = utils_1.checkValidWorkspace(createWorkspaceCwd, _1.createPackageCommand); 73 | gitName = utils_1.getGitConfig("user.name"); 74 | mockData = utils_1.getInternalExampleArgs("package"); 75 | _b = mockData; 76 | if (_b) return [3 /*break*/, 2]; 77 | return [4 /*yield*/, inquirer_1.prompt([ 78 | !packageName && { 79 | name: "packageName", 80 | message: utils_1.getCommandDescriptionForPrompt(_1.createPackageCommand, "--package-name"), 81 | type: "input", 82 | validate: function (value) { 83 | return /^[A-Za-z0-9-_]+$/.test(value) ? true : "Your package slug should only contain [A-Za-z0-9-_]."; 84 | } 85 | }, 86 | !packageDesc && { 87 | name: "packageDesc", 88 | message: utils_1.getCommandDescriptionForPrompt(_1.createPackageCommand, "--package-desc"), 89 | type: "input" 90 | }, 91 | !author && { 92 | name: "author", 93 | message: utils_1.getCommandDescriptionForPrompt(_1.createPackageCommand, "--author"), 94 | type: "input", 95 | validate: utils_1.inquirerRequiredValidate, 96 | default: gitName 97 | }, 98 | !packageUri && { 99 | name: "packageUri", 100 | message: utils_1.getCommandDescriptionForPrompt(_1.createPackageCommand, "--package-uri"), 101 | type: "input", 102 | validate: function (value) { return (value ? utils_1.isValidUrl(value, true) : true); } 103 | }, 104 | !namespace && { 105 | name: "namespace", 106 | message: utils_1.getCommandDescriptionForPrompt(_1.createPackageCommand, "--namespace"), 107 | type: "input", 108 | validate: function (value) { 109 | var required = utils_1.inquirerRequiredValidate(value); 110 | if (required !== true) { 111 | return required; 112 | } 113 | return utils_1.isValidPhpNamespace(value) ? true : "This is not a valid PHP namespace."; 114 | }, 115 | default: function (dAnswers) { 116 | var useThis = (dAnswers.author || author); 117 | var useSlug = (dAnswers.packageName || packageName); 118 | return useThis && useSlug 119 | ? utils_1.slugCamelCase(slugify_1.default(useThis, { 120 | lower: true 121 | }), true) + 122 | "\\" + 123 | utils_1.slugCamelCase(useSlug, true) 124 | : undefined; 125 | } 126 | }, 127 | !abbreviation && { 128 | name: "abbreviation", 129 | message: utils_1.getCommandDescriptionForPrompt(_1.createPackageCommand, "--abbreviation"), 130 | type: "input", 131 | validate: function (value) { 132 | var required = utils_1.inquirerRequiredValidate(value); 133 | if (required !== true) { 134 | return required; 135 | } 136 | return /^[A-Za-z_]+$/.test(value) 137 | ? true 138 | : "This is not a valid abbreviation (allowed: [A-Za-z_])."; 139 | }, 140 | default: function (dAnswers) { 141 | var result = (dAnswers.packageName || packageName).replace(/-/g, "_"); 142 | var availableFirstLetters = result.match(/_(.)/g); 143 | if (availableFirstLetters && availableFirstLetters.length > 1) { 144 | return result.charAt(0) + availableFirstLetters.map(function (o) { return o.slice(1); }).join(""); 145 | } 146 | return result; 147 | } 148 | } 149 | ].filter(Boolean))]; 150 | case 1: 151 | _b = (_c.sent()); 152 | _c.label = 2; 153 | case 2: 154 | answers = _b; 155 | // If there is no package name given via CLI also ask for email marketing 156 | return [4 /*yield*/, misc_1.newsletterPrompt(!mockData && !packageName)]; 157 | case 3: 158 | // If there is no package name given via CLI also ask for email marketing 159 | _c.sent(); 160 | _c.label = 4; 161 | case 4: 162 | _c.trys.push([4, 6, , 7]); 163 | parsed = __assign({ cwd: cwd, 164 | packageName: packageName, 165 | packageDesc: packageDesc, 166 | author: author, 167 | packageUri: packageUri, 168 | namespace: namespace, 169 | abbreviation: abbreviation }, answers); 170 | parsed.namespace = parsed.namespace.replace(/\\\\/g, "\\"); 171 | parsed = utils_1.caseAll(parsed, [], ["abbreviation", "packageName", "packageUri"]); 172 | return [4 /*yield*/, _1.createPackageExecute(root, parsed)]; 173 | case 5: 174 | _c.sent(); 175 | return [3 /*break*/, 7]; 176 | case 6: 177 | e_1 = _c.sent(); 178 | utils_1.logError(e_1.toString()); 179 | return [3 /*break*/, 7]; 180 | case 7: return [2 /*return*/]; 181 | } 182 | }); 183 | }); 184 | } 185 | exports.createPackagePrompt = createPackagePrompt; 186 | -------------------------------------------------------------------------------- /lib/create-plugin/applyPhpConstants.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var glob_1 = __importDefault(require("glob")); 7 | var utils_1 = require("../utils"); 8 | /** 9 | * Apply PHP constants to the available *.php files. Also adjust UtilsProvider trait. 10 | * 11 | * @param createPluginCwd 12 | * @param appliedTemplates 13 | * @param constantPrefix 14 | */ 15 | function applyPhpConstants(createPluginCwd, appliedTemplates, constantPrefix) { 16 | utils_1.logProgress("Get and apply all your PHP constants to the *.php files..."); 17 | // Find constants 18 | var m; 19 | var regex = /define\('([^']+)/g; 20 | var constantList = []; 21 | // tslint:disable-next-line: no-conditional-assignment 22 | while ((m = regex.exec(appliedTemplates.indexPhpContent)) !== null) { 23 | if (m.index === regex.lastIndex) { 24 | regex.lastIndex++; 25 | } 26 | m.forEach(function (match, groupIndex) { 27 | if (groupIndex === 1) { 28 | constantList.push(match); 29 | } 30 | }); 31 | } 32 | // Search & Replace constants 33 | var phpFiles = glob_1.default.sync("src/**/*.php", { cwd: createPluginCwd, absolute: true }); 34 | constantList.forEach(function (constant) { 35 | return utils_1.searchAndReplace(phpFiles, new RegExp("WPRJSS" + constant.slice(constantPrefix.length), "g"), constant); 36 | }); 37 | utils_1.searchAndReplace(glob_1.default.sync("src/inc/base/UtilsProvider.php", { cwd: createPluginCwd, absolute: true }), /'WPRJSS'/g, "'" + constantPrefix + "'"); 38 | utils_1.logSuccess("Successfully applied the following constants which you can use now - read more about them in WP ReactJS Starter documentation:\n - " + constantList.join("\n - ")); 39 | } 40 | exports.applyPhpConstants = applyPhpConstants; 41 | -------------------------------------------------------------------------------- /lib/create-plugin/applyPhpConstantsAndNamespace.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var glob_1 = __importDefault(require("glob")); 7 | var utils_1 = require("../utils"); 8 | var misc_1 = require("../misc"); 9 | /** 10 | * Apply PHP constants to the available *.php files. Also adjust 11 | * the PHP autoloading namespace (+ UtilsProvider trait). 12 | * 13 | * @param createPluginCwd 14 | * @param appliedTemplates 15 | * @param constantPrefix 16 | */ 17 | function applyPhpConstantsAndNamespace(createPluginCwd, appliedTemplates, constantPrefix, namespace) { 18 | utils_1.logProgress("Get and apply all your PHP constants to the *.php files..."); 19 | // Find constants 20 | var m; 21 | var regex = /define\('([^']+)/g; 22 | var constantList = []; 23 | // tslint:disable-next-line: no-conditional-assignment 24 | while ((m = regex.exec(appliedTemplates.indexPhpContent)) !== null) { 25 | if (m.index === regex.lastIndex) { 26 | regex.lastIndex++; 27 | } 28 | m.forEach(function (match, groupIndex) { 29 | if (groupIndex === 1) { 30 | constantList.push(match); 31 | } 32 | }); 33 | } 34 | // Search & Replace constants 35 | var phpFiles = glob_1.default.sync("src/**/*.php", { cwd: createPluginCwd, absolute: true }); 36 | constantList.forEach(function (constant) { 37 | return utils_1.searchAndReplace(phpFiles, new RegExp("WPRJSS" + constant.slice(constantPrefix.length), "g"), constant); 38 | }); 39 | utils_1.searchAndReplace(glob_1.default.sync("src/inc/base/UtilsProvider.php", { cwd: createPluginCwd, absolute: true }), /'WPRJSS'/g, "'" + constantPrefix + "'"); 40 | utils_1.logSuccess("Successfully applied the following constants which you can use now - read more about them in WP ReactJS Starter documentation:\n - " + constantList.join("\n - ")); 41 | // Apply the namespace 42 | misc_1.applyPhpNamespace(createPluginCwd, namespace, "plugin"); 43 | } 44 | exports.applyPhpConstantsAndNamespace = applyPhpConstantsAndNamespace; 45 | -------------------------------------------------------------------------------- /lib/create-plugin/applyPhpFunctions.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var chalk_1 = __importDefault(require("chalk")); 7 | var glob_1 = __importDefault(require("glob")); 8 | var utils_1 = require("../utils"); 9 | /** 10 | * Find PHP functions starting with wprjss_skip and replace them with the 11 | * correct prefix (depending on constant prefix). 12 | * 13 | * @param createPluginCwd 14 | * @param constantPrefix 15 | */ 16 | function applyPhpFunctions(createPluginCwd, constantPrefix) { 17 | var functionPrefix = constantPrefix.toLowerCase(); 18 | utils_1.logProgress("Find PHP functions and replace with " + chalk_1.default.underline(functionPrefix) + " prefix..."); 19 | var phpFiles = glob_1.default.sync("src/**/*.php", { cwd: createPluginCwd, absolute: true }); 20 | utils_1.searchAndReplace(phpFiles, /wprjss_skip/g, functionPrefix + "_skip"); 21 | } 22 | exports.applyPhpFunctions = applyPhpFunctions; 23 | -------------------------------------------------------------------------------- /lib/create-plugin/applyPromptsToTemplates.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var chalk_1 = __importDefault(require("chalk")); 7 | var path_1 = require("path"); 8 | var utils_1 = require("../utils"); 9 | var fs_1 = require("fs"); 10 | /** 11 | * Create template files with the given prompt values. 12 | * 13 | * @param rootName 14 | * @param createPluginCwd 15 | * @param templates 16 | * @param input 17 | * @returns 18 | */ 19 | function applyPromptsToTemplates(rootName, createPluginCwd, templates, input) { 20 | var applyTemplate = function (tmpl) { 21 | var mod = tmpl; 22 | Object.entries(input).forEach(function (_a) { 23 | var key = _a[0], value = _a[1]; 24 | if (key === "namespace") { 25 | value = value.replace(/\\/g, "\\\\"); 26 | } 27 | mod = mod.replace(new RegExp("\\$\\{" + key + "\\}", "g"), value); 28 | }); 29 | mod = mod.replace(new RegExp("\\$\\{rootName\\}", "g"), rootName); 30 | return mod; 31 | }; 32 | var indexPhpFile = path_1.resolve(createPluginCwd, "src/index.php"); 33 | var readmeTxtFile = path_1.resolve(createPluginCwd, "wordpress.org/README.wporg.txt"); 34 | var indexPhpContent = applyTemplate(templates["index.php"]); 35 | var readmeTxtContent = applyTemplate(templates["readme.wporg.txt"]); 36 | fs_1.writeFileSync(indexPhpFile, indexPhpContent, { encoding: utils_1.DEFAULT_ENCODING }); 37 | utils_1.logSuccess("Successfully created main plugin file " + chalk_1.default.underline(indexPhpFile)); 38 | fs_1.writeFileSync(readmeTxtFile, readmeTxtContent, { encoding: utils_1.DEFAULT_ENCODING }); 39 | utils_1.logSuccess("Successfully created readme file for wordpress.org " + chalk_1.default.underline(readmeTxtFile)); 40 | return { 41 | indexPhpFile: indexPhpFile, 42 | readmeTxtFile: readmeTxtFile, 43 | indexPhpContent: indexPhpContent, 44 | readmeTxtContent: readmeTxtContent 45 | }; 46 | } 47 | exports.applyPromptsToTemplates = applyPromptsToTemplates; 48 | -------------------------------------------------------------------------------- /lib/create-plugin/applySlug.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __spreadArrays = (this && this.__spreadArrays) || function () { 3 | for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; 4 | for (var r = Array(s), k = 0, i = 0; i < il; i++) 5 | for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) 6 | r[k] = a[j]; 7 | return r; 8 | }; 9 | var __importDefault = (this && this.__importDefault) || function (mod) { 10 | return (mod && mod.__esModule) ? mod : { "default": mod }; 11 | }; 12 | Object.defineProperty(exports, "__esModule", { value: true }); 13 | var chalk_1 = __importDefault(require("chalk")); 14 | var glob_1 = __importDefault(require("glob")); 15 | var utils_1 = require("../utils"); 16 | /** 17 | * Apply the slug to different files where needed. Also apply the main root slug 18 | * in the plugin so the utils package for example can be correctly linked and resolved. 19 | * 20 | * @param workspace 21 | * @param createPluginCwd 22 | * @param slug 23 | */ 24 | function applySlug(workspace, createPluginCwd, slug) { 25 | utils_1.logProgress("Find various code places and replace the slug with " + chalk_1.default.underline(slug) + "..."); 26 | var globFiles = function (pattern) { return glob_1.default.sync(pattern, { cwd: createPluginCwd, absolute: true }); }; 27 | var files = __spreadArrays(globFiles("devops/**/*.{yml,sh}"), globFiles("devops/.gitlab/**/*.yml"), globFiles("devops/.gitlab/.gitlab-ci.yml"), globFiles("package.json"), globFiles("src/**/*.php")); 28 | utils_1.searchAndReplace(files, /wp-reactjs-starter/g, slug); 29 | // Get root name and replace wp-reactjs-multi-starter 30 | utils_1.logProgress("Find various code places and replace the root package name with " + chalk_1.default.underline(workspace) + "..."); 31 | var multiStarterFiles = __spreadArrays(globFiles("src/public/ts/**/*.tsx"), globFiles("test/jest/**/*.tsx"), globFiles("{composer,package}.json"), globFiles("composer.lock"), globFiles("src/index.php")); 32 | utils_1.searchAndReplace(multiStarterFiles, /wp-reactjs-multi-starter/g, workspace); 33 | } 34 | exports.applySlug = applySlug; 35 | -------------------------------------------------------------------------------- /lib/create-plugin/copyTemplates.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var rimraf_1 = __importDefault(require("rimraf")); 7 | var chalk_1 = __importDefault(require("chalk")); 8 | var path_1 = require("path"); 9 | var utils_1 = require("../utils"); 10 | var fs_1 = require("fs"); 11 | var fs_extra_1 = require("fs-extra"); 12 | /** 13 | * Copy templates to the given path and read template files. 14 | * 15 | * @param createPluginCwd 16 | * @returns 17 | */ 18 | function copyTemplates(createPluginCwd) { 19 | utils_1.logProgress("Create new plugin from template..."); 20 | var templateCwd = path_1.resolve(createPluginCwd, "../..", utils_1.FOLDER_CWRA, "template"); 21 | fs_extra_1.copySync(templateCwd, createPluginCwd); 22 | var templates = { 23 | "index.php": fs_1.readFileSync(path_1.resolve(templateCwd, "../grunt-index-php.tmpl"), { encoding: utils_1.DEFAULT_ENCODING }), 24 | "readme.wporg.txt": fs_1.readFileSync(path_1.resolve(templateCwd, "../grunt-readme-txt.tmpl"), { 25 | encoding: utils_1.DEFAULT_ENCODING 26 | }) 27 | }; 28 | // Delete language folders 29 | rimraf_1.default.sync(path_1.resolve(createPluginCwd, "src/languages")); 30 | rimraf_1.default.sync(path_1.resolve(createPluginCwd, "src/public/languages")); 31 | utils_1.logSuccess("Successfully created plugin folder " + chalk_1.default.underline(createPluginCwd)); 32 | return templates; 33 | } 34 | exports.copyTemplates = copyTemplates; 35 | -------------------------------------------------------------------------------- /lib/create-plugin/execute.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (_) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | var __importDefault = (this && this.__importDefault) || function (mod) { 39 | return (mod && mod.__esModule) ? mod : { "default": mod }; 40 | }; 41 | Object.defineProperty(exports, "__esModule", { value: true }); 42 | var path_1 = require("path"); 43 | var fs_1 = require("fs"); 44 | var _1 = require("./"); 45 | var utils_1 = require("../utils"); 46 | var chalk_1 = __importDefault(require("chalk")); 47 | var execa_1 = __importDefault(require("execa")); 48 | var misc_1 = require("../misc"); 49 | /** 50 | * Generate a new plugin from the template. All validations are done in createPluginPrompt. 51 | * 52 | * @param root 53 | * @param input 54 | * @param fromWorkspace 55 | * @returns 56 | * @throws 57 | */ 58 | function createPluginExecute(root, input, fromWorkspace) { 59 | if (fromWorkspace === void 0) { fromWorkspace = false; } 60 | return __awaiter(this, void 0, void 0, function () { 61 | var createPluginCwd, templates, appliedTemplates; 62 | return __generator(this, function (_a) { 63 | createPluginCwd = path_1.resolve(input.cwd, "plugins", input.slug); 64 | // Strictly do not override an existing plugin!! 65 | if (fs_1.existsSync(createPluginCwd)) { 66 | throw new Error("You already have a plugin with slug " + input.slug + "."); 67 | } 68 | templates = _1.copyTemplates(createPluginCwd); 69 | appliedTemplates = _1.applyPromptsToTemplates(root.name, createPluginCwd, templates, input); 70 | _1.applyPhpConstants(createPluginCwd, appliedTemplates, input.constantPrefix); 71 | _1.applyPhpFunctions(createPluginCwd, input.constantPrefix); 72 | _1.applySlug(root.name, createPluginCwd, input.slug); 73 | misc_1.applyGitLabCi(createPluginCwd, input.constantPrefix, "wprjss"); 74 | misc_1.applyPackageJson(root.name, createPluginCwd, { 75 | author: input.author, 76 | description: input.pluginDesc, 77 | homepage: input.pluginUri, 78 | version: input.pluginVersion 79 | }, fromWorkspace ? "1.0.0" : true); 80 | misc_1.modifyRootGitLabCiInclude("add", input.cwd, input.slug, "plugins"); 81 | utils_1.generateLicenseFile(createPluginCwd, input.author, input.pluginDesc); 82 | utils_1.logSuccess("Successfully created plugin " + input.pluginName + " in " + chalk_1.default.underline(createPluginCwd)); 83 | if (!fromWorkspace) { 84 | misc_1.applyPhpNamespace(createPluginCwd, input.namespace, "plugin"); 85 | utils_1.logProgress("Bootstrap and link new plugin..."); 86 | execa_1.default.sync("yarn", ["bootstrap"], { cwd: input.cwd, stdio: "inherit" }); 87 | execa_1.default.sync("yarn", ["lerna", "link"], { cwd: input.cwd, stdio: "inherit" }); 88 | utils_1.runThirdPartyLicenseForPackage(createPluginCwd); 89 | misc_1.regenerateLaunchJson(input.cwd); 90 | // First builds 91 | _1.preInstallationBuilds(createPluginCwd); 92 | utils_1.logProgress("Rebuild the development environment, afterwards you can activate your new plugin..."); 93 | execa_1.default("yarn", ["docker:start"], { cwd: input.cwd, stdio: "inherit" }); 94 | } 95 | return [2 /*return*/, createPluginCwd]; 96 | }); 97 | }); 98 | } 99 | exports.createPluginExecute = createPluginExecute; 100 | -------------------------------------------------------------------------------- /lib/create-plugin/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function __export(m) { 3 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; 4 | } 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | __export(require("./applyPhpConstants")); 7 | __export(require("./applyPhpFunctions")); 8 | __export(require("./applyPromptsToTemplates")); 9 | __export(require("./applySlug")); 10 | __export(require("./copyTemplates")); 11 | __export(require("./installPlugin")); 12 | __export(require("./execute")); 13 | __export(require("./program")); 14 | __export(require("./prompt")); 15 | -------------------------------------------------------------------------------- /lib/create-plugin/installPlugin.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var utils_1 = require("../utils"); 7 | var execa_1 = __importDefault(require("execa")); 8 | /** 9 | * Do some basic builds for a plugin or package. 10 | * 11 | * @param answers 12 | */ 13 | function preInstallationBuilds(createPluginCwd) { 14 | utils_1.logProgress("Start initial build process..."); 15 | execa_1.default.sync("yarn", ["build"], { cwd: createPluginCwd, stdio: "inherit" }); 16 | utils_1.logSuccess("Successfully created first build"); 17 | utils_1.logProgress("Run tests..."); 18 | execa_1.default.sync("yarn", ["test"], { cwd: createPluginCwd, stdio: "inherit" }); 19 | utils_1.logSuccess("Successfully run tests"); 20 | utils_1.logProgress("Start i18n generation..."); 21 | execa_1.default.sync("yarn", ["i18n:generate:backend"], { cwd: createPluginCwd, stdio: "inherit" }); 22 | execa_1.default.sync("yarn", ["i18n:generate:frontend"], { cwd: createPluginCwd, stdio: "inherit" }); 23 | utils_1.logSuccess("Successfully generate .pot files in your plugin"); 24 | } 25 | exports.preInstallationBuilds = preInstallationBuilds; 26 | -------------------------------------------------------------------------------- /lib/create-plugin/program.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var commander_1 = require("commander"); 4 | var _1 = require("./"); 5 | var createPluginCommand = commander_1.command("create-plugin") 6 | .description("Create a new plugin within your workspace which you created previously.") 7 | .option("--cwd ", "The path to the workspace where the plugin should be created", process.cwd()) 8 | .option("--plugin-name ", "How should your plugin be named?") 9 | .option("--slug ", "What's the plugin slug (similar to folder name inside wp-content/plugins)?") 10 | .option("--plugin-uri ", "What's your plugins' homepage URL?") 11 | .option("--plugin-desc ", "Give a one-line plugin description?") 12 | .option("--author ", "Who is the plugin author (e. g. your wordpress.org username)?") 13 | .option("--author-uri ", "What's your author homepage URL?") 14 | .option("--plugin-version ", "What should be the initial version of the plugin?") 15 | .option("--min-php ", "What's the minimum required PHP version (minimum of 7.0 required for the boilerplate)?") 16 | .option("--min-wp ", "What's the minimum required WordPress version (minimum of 5.2 required for the boilerplate)?") 17 | .option("--namespace ", "What's the PHP Namespace (e. g. 'MatthiasWeb\\WPRJSS')?") 18 | .option("--opt-prefix ", "What's the WordPress options (wp_options) names prefix?") 19 | .option("--db-prefix ", "What's the WordPress database tables prefix?") 20 | .option("--constant-prefix ", "What's the PHP constants prefix?") 21 | .action(function (args) { return _1.createPluginPrompt(args); }); 22 | exports.createPluginCommand = createPluginCommand; 23 | -------------------------------------------------------------------------------- /lib/create-workspace/applyPhpUtils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var utils_1 = require("../utils"); 7 | var glob_1 = __importDefault(require("glob")); 8 | var chalk_1 = __importDefault(require("chalk")); 9 | var misc_1 = require("../misc"); 10 | var path_1 = require("path"); 11 | /** 12 | * Generate a namespace for the first created plugin and 13 | * generate accordingly a namespace for the utils PHP package. 14 | */ 15 | function applyPhpUtils(pluginNamespace, createPluginCwd) { 16 | var splitNs = pluginNamespace.split("\\"); 17 | var utilsNamespace = (splitNs.length > 1 ? splitNs.slice(0, -1).join("\\") : pluginNamespace) + "\\Utils"; 18 | utils_1.logProgress("Apply namespace to utils package " + chalk_1.default.underline(utilsNamespace) + "..."); 19 | var globFiles = function (pattern) { return glob_1.default.sync(pattern, { cwd: createPluginCwd, absolute: true }); }; 20 | utils_1.searchAndReplace(globFiles("composer.lock"), /MatthiasWeb\\\\Utils/g, utilsNamespace.replace(/\\/g, "\\\\")); 21 | utils_1.searchAndReplace(globFiles("src/inc/**/*.php"), /MatthiasWeb\\Utils/g, utilsNamespace); 22 | utils_1.logSuccess("Successfully applied PHP utils namespace to plugin!"); 23 | // Apply to utils package itself 24 | misc_1.applyPhpNamespace(path_1.resolve(createPluginCwd, "../../packages/utils"), utilsNamespace, "utils"); 25 | return utilsNamespace; 26 | } 27 | exports.applyPhpUtils = applyPhpUtils; 28 | -------------------------------------------------------------------------------- /lib/create-workspace/applyPorts.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var chalk_1 = __importDefault(require("chalk")); 7 | var utils_1 = require("../utils"); 8 | var glob_1 = __importDefault(require("glob")); 9 | /** 10 | * Apply ports to the local docker-compose environment. 11 | * 12 | * @param portWp 13 | * @param portPma 14 | * @param createCwd 15 | */ 16 | function applyPorts(portWp, portPma, createCwd) { 17 | utils_1.logProgress("Apply ports " + chalk_1.default.underline(portWp) + " (WP) and " + chalk_1.default.underline(portPma) + " (PMA) for local development..."); 18 | var composeFiles = glob_1.default.sync("devops/docker-compose/docker-compose.local.yml", { 19 | cwd: createCwd, 20 | absolute: true 21 | }); 22 | var shFiles = glob_1.default.sync("devops/scripts/*.sh", { cwd: createCwd, absolute: true }); 23 | utils_1.searchAndReplace(composeFiles, /"8080:80"/g, "\"" + portWp + ":80\""); 24 | utils_1.searchAndReplace(composeFiles, /"8079:80"/g, "\"" + portPma + ":80\""); 25 | utils_1.searchAndReplace(shFiles, /localhost:8080/g, "localhost:" + portWp); 26 | } 27 | exports.applyPorts = applyPorts; 28 | -------------------------------------------------------------------------------- /lib/create-workspace/applyWorkspaceName.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __spreadArrays = (this && this.__spreadArrays) || function () { 3 | for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; 4 | for (var r = Array(s), k = 0, i = 0; i < il; i++) 5 | for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) 6 | r[k] = a[j]; 7 | return r; 8 | }; 9 | var __importDefault = (this && this.__importDefault) || function (mod) { 10 | return (mod && mod.__esModule) ? mod : { "default": mod }; 11 | }; 12 | Object.defineProperty(exports, "__esModule", { value: true }); 13 | var chalk_1 = __importDefault(require("chalk")); 14 | var glob_1 = __importDefault(require("glob")); 15 | var utils_1 = require("../utils"); 16 | /** 17 | * Apply workspace name to the given folder. 18 | * 19 | * @param workspace 20 | * @param createCwd 21 | */ 22 | function applyWorkspaceName(workspace, createCwd) { 23 | utils_1.logProgress("Apply workspace name " + chalk_1.default.underline(workspace) + "..."); 24 | var globFiles = function (pattern) { return glob_1.default.sync(pattern, { cwd: createCwd, absolute: true }); }; 25 | var workspaceFiles = __spreadArrays(globFiles(".gitlab-ci.yml"), globFiles("package.json"), globFiles("{plugins,packages}/*/package.json")); 26 | utils_1.searchAndReplace(workspaceFiles, /wp-reactjs-multi-starter/g, workspace); 27 | utils_1.logSuccess("Workspace is now branded as " + chalk_1.default.underline(workspace)); 28 | } 29 | exports.applyWorkspaceName = applyWorkspaceName; 30 | -------------------------------------------------------------------------------- /lib/create-workspace/completeWorkspace.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (_) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | var __importDefault = (this && this.__importDefault) || function (mod) { 39 | return (mod && mod.__esModule) ? mod : { "default": mod }; 40 | }; 41 | Object.defineProperty(exports, "__esModule", { value: true }); 42 | var execa_1 = __importDefault(require("execa")); 43 | var chalk_1 = __importDefault(require("chalk")); 44 | var _1 = require("./"); 45 | var utils_1 = require("../utils"); 46 | var create_plugin_1 = require("../create-plugin"); 47 | var terminal_link_1 = __importDefault(require("terminal-link")); 48 | var misc_1 = require("../misc"); 49 | /** 50 | * The workspace and initial plugin is created, do some installation process! 51 | * 52 | * @param createPluginCwd 53 | * @param createWorkspaceCwd 54 | * @param workspaceData 55 | * @param gitlabProject 56 | */ 57 | function completeWorkspace(createPluginCwd, createWorkspaceCwd, createUtilsCwd, gitlabProject) { 58 | return __awaiter(this, void 0, void 0, function () { 59 | return __generator(this, function (_a) { 60 | // Install dependencies 61 | utils_1.logProgress("Bootstrap monorepo and download dependencies..."); 62 | execa_1.default.sync("yarn", ["bootstrap"], { cwd: createWorkspaceCwd, stdio: "inherit" }); 63 | _1.runThirdPartyLicenses(createWorkspaceCwd); 64 | misc_1.regenerateLaunchJson(createWorkspaceCwd); 65 | // Prompt first build processes 66 | utils_1.logSuccess("\n\nThe workspace is now usable. For first usage it is recommend to do some first tests and builds to check all is working fine."); 67 | create_plugin_1.preInstallationBuilds(createUtilsCwd); 68 | create_plugin_1.preInstallationBuilds(createPluginCwd); 69 | // Start VSCode if available 70 | try { 71 | execa_1.default("code", [createWorkspaceCwd], { 72 | detached: true 73 | }); 74 | } 75 | catch (e) { 76 | // Silence is golden. 77 | } 78 | // Push changes 79 | if (gitlabProject) { 80 | utils_1.logProgress("Push complete code to repository..."); 81 | execa_1.default.sync("git", ["add", "-A"], { cwd: createWorkspaceCwd, stdio: "inherit" }); 82 | execa_1.default.sync("git", ["commit", "-m 'chore: initial commit'", "--no-verify"], { 83 | cwd: createWorkspaceCwd, 84 | stdio: "inherit" 85 | }); 86 | execa_1.default.sync("git", ["push", "origin", "develop"], { cwd: createWorkspaceCwd, stdio: "inherit" }); 87 | utils_1.logSuccess("Successfully pushed code, see your CI/CD pipeline running " + terminal_link_1.default("here", gitlabProject.web_url + "/pipelines")); 88 | } 89 | utils_1.logProgress("Initially start the development environment with " + chalk_1.default.underline("yarn docker:start") + "..."); 90 | execa_1.default("yarn", ["docker:start"], { cwd: createWorkspaceCwd, stdio: "inherit" }); 91 | return [2 /*return*/]; 92 | }); 93 | }); 94 | } 95 | exports.completeWorkspace = completeWorkspace; 96 | -------------------------------------------------------------------------------- /lib/create-workspace/createDotEnv.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var fs_1 = require("fs"); 4 | var path_1 = require("path"); 5 | var utils_1 = require("../utils"); 6 | /** 7 | * Create a predefined .env file. 8 | * 9 | * @param createCwd 10 | * @param input 11 | */ 12 | function createDotEnv(createCwd, input) { 13 | var dotEnvFile = path_1.resolve(createCwd, ".env"); 14 | var pairs = {}; 15 | if (input.remote) { 16 | pairs["WP_LOCAL_INSTALL_DIR"] = input.remote; 17 | } 18 | if (Object.keys(pairs).length > 0) { 19 | var content_1 = ""; 20 | Object.keys(pairs).forEach(function (key) { return (content_1 += key + "=" + pairs[key] + "\n"); }); 21 | fs_1.writeFileSync(dotEnvFile, content_1, { encoding: utils_1.DEFAULT_ENCODING }); 22 | } 23 | } 24 | exports.createDotEnv = createDotEnv; 25 | -------------------------------------------------------------------------------- /lib/create-workspace/createGitFolder.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var chalk_1 = __importDefault(require("chalk")); 7 | var execa_1 = __importDefault(require("execa")); 8 | var path_1 = require("path"); 9 | var rimraf_1 = __importDefault(require("rimraf")); 10 | var utils_1 = require("../utils"); 11 | var fs_extra_1 = require("fs-extra"); 12 | /** 13 | * Clone the repository and disconnect it. 14 | * 15 | * @param checkout 16 | * @param repository 17 | * @param createCwd 18 | * @param gitlabProject 19 | */ 20 | function createGitFolder(checkout, repository, createCwd, gitlabProject) { 21 | // Start cloning the repository 22 | utils_1.logProgress("Download repository " + chalk_1.default.underline(repository) + " ref " + chalk_1.default.underline(checkout) + "..."); 23 | execa_1.default.sync("git", ["clone", repository, path_1.basename(createCwd)], { stdio: "inherit" }); 24 | execa_1.default.sync("git", ["checkout", checkout], { cwd: createCwd, stdio: "inherit" }); 25 | rimraf_1.default.sync(path_1.resolve(createCwd, ".git")); 26 | // If there is a GitLab project, resolve the SSH checkout and move the .git configuration to the original folder 27 | if (gitlabProject) { 28 | var downloadCloneTarget = createCwd + "-temp"; 29 | utils_1.logProgress("Initialize into new repository " + chalk_1.default.underline(gitlabProject.ssh_url_to_repo) + "..."); 30 | execa_1.default.sync("git", ["clone", gitlabProject.ssh_url_to_repo, path_1.basename(downloadCloneTarget)], { 31 | stdio: "inherit" 32 | }); 33 | execa_1.default.sync("git", ["checkout", "develop"], { cwd: downloadCloneTarget, stdio: "inherit" }); 34 | fs_extra_1.moveSync(downloadCloneTarget + "/.git", createCwd + "/.git"); 35 | rimraf_1.default.sync(downloadCloneTarget); 36 | } 37 | // Remove root LICENSE file 38 | fs_extra_1.unlinkSync(path_1.resolve(createCwd, "LICENSE")); 39 | utils_1.logSuccess("Workspace successfully created in " + chalk_1.default.underline(createCwd)); 40 | } 41 | exports.createGitFolder = createGitFolder; 42 | -------------------------------------------------------------------------------- /lib/create-workspace/execute.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (_) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | Object.defineProperty(exports, "__esModule", { value: true }); 39 | var path_1 = require("path"); 40 | var _1 = require("./"); 41 | var create_plugin_1 = require("../create-plugin"); 42 | var create_package_1 = require("../create-package"); 43 | var utils_1 = require("../utils"); 44 | var misc_1 = require("../misc"); 45 | var createDotEnv_1 = require("./createDotEnv"); 46 | /** 47 | * Generate a new workspace from a given repository and disconnect it. 48 | * Also do some garbage collection and movements for other commands. 49 | */ 50 | function createWorkspaceExecute(input) { 51 | return __awaiter(this, void 0, void 0, function () { 52 | var createCwd, utilsPath, gitlabProjectCreator, gitLabProject; 53 | var _this = this; 54 | return __generator(this, function (_a) { 55 | switch (_a.label) { 56 | case 0: 57 | createCwd = path_1.resolve(process.cwd(), input.workspace); 58 | utilsPath = path_1.resolve(createCwd, "packages/utils"); 59 | return [4 /*yield*/, _1.promptGitLab(input.workspace)]; 60 | case 1: 61 | gitlabProjectCreator = _a.sent(); 62 | // Run create-plugin command without installation (because this is done below) 63 | // So we have all prompts in one flow, awesome! 64 | return [4 /*yield*/, create_plugin_1.createPluginPrompt({ 65 | cwd: createCwd 66 | }, function () { return __awaiter(_this, void 0, void 0, function () { 67 | return __generator(this, function (_a) { 68 | switch (_a.label) { 69 | case 0: 70 | if (!(gitlabProjectCreator !== false)) return [3 /*break*/, 2]; 71 | return [4 /*yield*/, gitlabProjectCreator()]; 72 | case 1: 73 | gitLabProject = _a.sent(); 74 | _a.label = 2; 75 | case 2: 76 | _1.createGitFolder(input.checkout, input.repository, createCwd, gitLabProject); 77 | createDotEnv_1.createDotEnv(createCwd, input); 78 | _1.removeExamplePlugin(createCwd); 79 | _1.applyWorkspaceName(input.workspace, createCwd); 80 | _1.applyPorts(input.portWp, input.portPma, createCwd); 81 | return [2 /*return*/]; 82 | } 83 | }); 84 | }); }, function (createPluginCwd, pluginData) { return __awaiter(_this, void 0, void 0, function () { 85 | var splitNs, utilsNamespace; 86 | return __generator(this, function (_a) { 87 | switch (_a.label) { 88 | case 0: 89 | splitNs = pluginData.namespace.split("\\"); 90 | utilsNamespace = (splitNs.length > 1 ? splitNs.slice(0, -1).join("\\") : pluginData.namespace) + "\\Utils"; 91 | misc_1.applyPhpNamespace(utilsPath, utilsNamespace, "utils"); 92 | // Re-apply namespace of utils package for this plugin because before it did not know the namespace 93 | misc_1.applyPhpNamespace(createPluginCwd, pluginData.namespace, "plugin"); 94 | create_package_1.applyName(utilsPath, "utils"); 95 | misc_1.applyPackageJson(input.workspace, utilsPath, { 96 | author: pluginData.author, 97 | description: "Utility functionality for all your WordPress plugins.", 98 | homepage: pluginData.authorUri, 99 | version: "1.0.0" 100 | }, false); 101 | utils_1.generateLicenseFile(utilsPath, pluginData.author, pluginData.pluginDesc); 102 | // Complete 103 | return [4 /*yield*/, _1.completeWorkspace(createPluginCwd, createCwd, utilsPath, gitLabProject)]; 104 | case 1: 105 | // Complete 106 | _a.sent(); 107 | return [2 /*return*/]; 108 | } 109 | }); 110 | }); })]; 111 | case 2: 112 | // Run create-plugin command without installation (because this is done below) 113 | // So we have all prompts in one flow, awesome! 114 | _a.sent(); 115 | return [2 /*return*/]; 116 | } 117 | }); 118 | }); 119 | } 120 | exports.createWorkspaceExecute = createWorkspaceExecute; 121 | -------------------------------------------------------------------------------- /lib/create-workspace/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function __export(m) { 3 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; 4 | } 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | __export(require("./applyPorts")); 7 | __export(require("./runThirdPartyLicenses")); 8 | __export(require("./applyWorkspaceName")); 9 | __export(require("./checkDependencies")); 10 | __export(require("./completeWorkspace")); 11 | __export(require("./createGitFolder")); 12 | __export(require("./execute")); 13 | __export(require("./program")); 14 | __export(require("./prompt")); 15 | __export(require("./promptGitLab")); 16 | __export(require("./removeExamplePlugin")); 17 | __export(require("./createDotEnv")); 18 | -------------------------------------------------------------------------------- /lib/create-workspace/program.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var commander_1 = require("commander"); 4 | var _1 = require("./"); 5 | var createWorkspaceCommand = commander_1.command("create-workspace") 6 | .description("Creates a new workspace where your plugins are placed.") 7 | .option("-w, --workspace [value]", "What's the name of your workspace (similar to your future git repository name)?", "") 8 | .option("-r, --repository [value]", "The repository URL", "https://github.com/devowlio/wp-react-starter") 9 | .option("-g, --checkout [value]", "The checkout ref", "master") 10 | .option("--remote ", "Which URL should be used for WordPress development? Leave empty for `localhost`") 11 | .option("--port-wp ", "Which port should be used for the local WordPress development?", function (i) { return +i; }) 12 | .option("--port-pma ", "Which port should be used for the local phpMyAdmin development?", function (i) { return +i; }) 13 | .action(function (args) { return _1.createWorkspacePrompt(args); }); 14 | exports.createWorkspaceCommand = createWorkspaceCommand; 15 | -------------------------------------------------------------------------------- /lib/create-workspace/prompt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __assign = (this && this.__assign) || function () { 3 | __assign = Object.assign || function(t) { 4 | for (var s, i = 1, n = arguments.length; i < n; i++) { 5 | s = arguments[i]; 6 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) 7 | t[p] = s[p]; 8 | } 9 | return t; 10 | }; 11 | return __assign.apply(this, arguments); 12 | }; 13 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 14 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 15 | return new (P || (P = Promise))(function (resolve, reject) { 16 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 17 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 18 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 19 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 20 | }); 21 | }; 22 | var __generator = (this && this.__generator) || function (thisArg, body) { 23 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 24 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 25 | function verb(n) { return function (v) { return step([n, v]); }; } 26 | function step(op) { 27 | if (f) throw new TypeError("Generator is already executing."); 28 | while (_) try { 29 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 30 | if (y = 0, t) op = [op[0] & 2, t.value]; 31 | switch (op[0]) { 32 | case 0: case 1: t = op; break; 33 | case 4: _.label++; return { value: op[1], done: false }; 34 | case 5: _.label++; y = op[1]; op = [0]; continue; 35 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 36 | default: 37 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 38 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 39 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 40 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 41 | if (t[2]) _.ops.pop(); 42 | _.trys.pop(); continue; 43 | } 44 | op = body.call(thisArg, _); 45 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 46 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 47 | } 48 | }; 49 | Object.defineProperty(exports, "__esModule", { value: true }); 50 | var inquirer_1 = require("inquirer"); 51 | var _1 = require("./"); 52 | var utils_1 = require("../utils"); 53 | /** 54 | * Prompt for CLI arguments which are not passed. 55 | */ 56 | function createWorkspacePrompt(_a) { 57 | var workspace = _a.workspace, repository = _a.repository, checkout = _a.checkout, remote = _a.remote, portWp = _a.portWp, portPma = _a.portPma; 58 | return __awaiter(this, void 0, void 0, function () { 59 | var mockData, answers, _b, parsed, e_1; 60 | return __generator(this, function (_c) { 61 | switch (_c.label) { 62 | case 0: return [4 /*yield*/, _1.checkDependencies()]; 63 | case 1: 64 | _c.sent(); 65 | mockData = utils_1.getInternalExampleArgs("workspace"); 66 | _b = mockData; 67 | if (_b) return [3 /*break*/, 3]; 68 | return [4 /*yield*/, inquirer_1.prompt([ 69 | !workspace && { 70 | name: "workspace", 71 | message: utils_1.getCommandDescriptionForPrompt(_1.createWorkspaceCommand, "--workspace"), 72 | type: "input", 73 | validate: function (value) { 74 | if (value && /^[A-Za-z0-9-_]+$/.test(value)) { 75 | return true; 76 | } 77 | return "Your workspace name should only contain [A-Za-z0-9-_]"; 78 | } 79 | }, 80 | !remote && { 81 | name: "remote", 82 | message: utils_1.getCommandDescriptionForPrompt(_1.createWorkspaceCommand, "--remote"), 83 | type: "input", 84 | validate: function (value) { 85 | return !value ? true : utils_1.isValidUrl(value, true); 86 | } 87 | }, 88 | !portWp && { 89 | name: "portWp", 90 | message: utils_1.getCommandDescriptionForPrompt(_1.createWorkspaceCommand, "--port-wp"), 91 | type: "number", 92 | default: function (answers) { 93 | var useRemote = answers.remote || remote; 94 | if (useRemote) { 95 | return new URL(useRemote).port || 80; 96 | } 97 | return 8080; 98 | }, 99 | validate: utils_1.inquirerRequiredValidate 100 | }, 101 | !portPma && { 102 | name: "portPma", 103 | message: utils_1.getCommandDescriptionForPrompt(_1.createWorkspaceCommand, "--port-pma"), 104 | type: "number", 105 | default: function (answers) { 106 | var useThis = (answers.portWp || +portWp); 107 | return useThis > 0 ? useThis + 1 : 8079; 108 | }, 109 | validate: function (value, answers) { 110 | if (value === (answers.portWp || +portWp)) { 111 | return "You can not use the port twice."; 112 | } 113 | return true; 114 | } 115 | } 116 | ].filter(Boolean))]; 117 | case 2: 118 | _b = (_c.sent()); 119 | _c.label = 3; 120 | case 3: 121 | answers = _b; 122 | _c.label = 4; 123 | case 4: 124 | _c.trys.push([4, 6, , 7]); 125 | parsed = __assign({ workspace: workspace, repository: repository, checkout: checkout, remote: remote, portWp: portWp, portPma: portPma }, answers); 126 | return [4 /*yield*/, _1.createWorkspaceExecute(utils_1.caseAll(parsed, [], ["workspace"]))]; 127 | case 5: 128 | _c.sent(); 129 | return [3 /*break*/, 7]; 130 | case 6: 131 | e_1 = _c.sent(); 132 | utils_1.logError(e_1.toString()); 133 | return [3 /*break*/, 7]; 134 | case 7: return [2 /*return*/]; 135 | } 136 | }); 137 | }); 138 | } 139 | exports.createWorkspacePrompt = createWorkspacePrompt; 140 | -------------------------------------------------------------------------------- /lib/create-workspace/promptGitLab.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (_) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | var __importDefault = (this && this.__importDefault) || function (mod) { 39 | return (mod && mod.__esModule) ? mod : { "default": mod }; 40 | }; 41 | Object.defineProperty(exports, "__esModule", { value: true }); 42 | var execa_1 = __importDefault(require("execa")); 43 | var gitlab_1 = require("gitlab"); 44 | var inquirer_1 = require("inquirer"); 45 | var utils_1 = require("../utils"); 46 | var terminal_link_1 = __importDefault(require("terminal-link")); 47 | var chalk_1 = __importDefault(require("chalk")); 48 | /** 49 | * Prompt for GitLab auth and create the repository in the given group / namespace. 50 | * It exits automatically if something went wrong (for example SSH check) 51 | * 52 | * @param workspace 53 | * @returns `false` or `any` representing the new project 54 | */ 55 | function promptGitLab(workspace) { 56 | return __awaiter(this, void 0, void 0, function () { 57 | var doGitlab, _a, host, token, api_1, groups, user, namespaceId_1, group, creator; 58 | var _this = this; 59 | return __generator(this, function (_b) { 60 | switch (_b.label) { 61 | case 0: 62 | if (process.env.SKIP_PROMPT) { 63 | return [2 /*return*/, false]; 64 | } 65 | return [4 /*yield*/, inquirer_1.prompt([ 66 | { 67 | name: "doGitlab", 68 | type: "confirm", 69 | message: "Would you like to connect with your GitLab and automatically create and push to a new repository?" 70 | } 71 | ])]; 72 | case 1: 73 | doGitlab = (_b.sent()).doGitlab; 74 | if (!doGitlab) return [3 /*break*/, 7]; 75 | return [4 /*yield*/, inquirer_1.prompt([ 76 | { 77 | name: "host", 78 | type: "input", 79 | message: "What's your GitLab instance URL?", 80 | default: process.env.GITLAB_HOST || "https://gitlab.com", 81 | validate: function (value) { return utils_1.isValidUrl(value, true); } 82 | }, 83 | { 84 | name: "token", 85 | type: "input", 86 | message: "What's your " + terminal_link_1.default("personal token", "https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html") + " (click the link to learn how to obtain a token; token must have " + chalk_1.default.underline("api,write_repository") + " scope)?", 87 | default: process.env.GITLAB_TOKEN, 88 | validate: utils_1.inquirerRequiredValidate 89 | } 90 | ])]; 91 | case 2: 92 | _a = _b.sent(), host = _a.host, token = _a.token; 93 | // Check if SSH was setup correctly 94 | utils_1.logProgress("Check if SSH is setup correctly..."); 95 | try { 96 | execa_1.default.sync("ssh", ["-T", "git@" + host.replace(/^https?:\/\//, "")]); 97 | } 98 | catch (e) { 99 | utils_1.logError("SSH key is not setup for this host. Please checkout this " + terminal_link_1.default("documentation", "https://docs.gitlab.com/ee/ssh/") + ". You can rerun this command without creating a GitLab repository automatically."); 100 | process.exit(1); 101 | } 102 | api_1 = new gitlab_1.Gitlab({ 103 | host: host, 104 | token: token, 105 | rejectUnauthorized: true 106 | }); 107 | utils_1.logProgress("Loading available possible targets for the new repository..."); 108 | return [4 /*yield*/, api_1.Groups.all()]; 109 | case 3: 110 | groups = (_b.sent()); 111 | return [4 /*yield*/, api_1.Users.current()]; 112 | case 4: 113 | user = (_b.sent()); 114 | if (!(groups && groups.length)) return [3 /*break*/, 6]; 115 | return [4 /*yield*/, inquirer_1.prompt([ 116 | { 117 | name: "group", 118 | message: "Where in GitLab (Group) do you want to create the repository?", 119 | type: "list", 120 | choices: groups 121 | .map(function (g) { return ({ 122 | name: g.full_name, 123 | value: g 124 | }); }) 125 | .concat([ 126 | { 127 | name: "Assign to myself, " + user.username, 128 | value: "" 129 | } 130 | ]) 131 | } 132 | ])]; 133 | case 5: 134 | group = (_b.sent()).group; 135 | namespaceId_1 = group ? group.id : undefined; 136 | _b.label = 6; 137 | case 6: 138 | creator = function () { return __awaiter(_this, void 0, void 0, function () { 139 | var project; 140 | return __generator(this, function (_a) { 141 | switch (_a.label) { 142 | case 0: 143 | // Create the repository in the given namespace 144 | utils_1.logProgress("Create GitLab project..."); 145 | return [4 /*yield*/, api_1.Projects.create({ 146 | name: workspace, 147 | namespace_id: namespaceId_1 // eslint-disable-line @typescript-eslint/camelcase 148 | })]; 149 | case 1: 150 | project = (_a.sent()); 151 | utils_1.logSuccess("Successfully created project " + chalk_1.default.underline(project.name_with_namespace)); 152 | // Create the protected develop branch 153 | utils_1.logProgress("Setup repository..."); 154 | return [4 /*yield*/, api_1.Branches.create(project.id, "develop", "master")]; 155 | case 2: 156 | _a.sent(); 157 | return [4 /*yield*/, api_1.Projects.edit(project.id, { 158 | default_branch: "develop" // eslint-disable-line @typescript-eslint/camelcase 159 | })]; 160 | case 3: 161 | _a.sent(); 162 | utils_1.logSuccess("Successfully created default branch " + chalk_1.default.underline("develop")); 163 | return [2 /*return*/, project]; 164 | } 165 | }); 166 | }); }; 167 | return [2 /*return*/, creator]; 168 | case 7: return [2 /*return*/, false]; 169 | } 170 | }); 171 | }); 172 | } 173 | exports.promptGitLab = promptGitLab; 174 | -------------------------------------------------------------------------------- /lib/create-workspace/removeExamplePlugin.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var chalk_1 = __importDefault(require("chalk")); 7 | var path_1 = require("path"); 8 | var utils_1 = require("../utils"); 9 | var fs_1 = require("fs"); 10 | var fs_extra_1 = require("fs-extra"); 11 | var rimraf_1 = __importDefault(require("rimraf")); 12 | var misc_1 = require("../misc"); 13 | /** 14 | * Remove first initial plugin and move to create-wp-react-app for others commands. 15 | * Also alter the .gitlab-ci.yml includes and CHANGELOG.md. 16 | * 17 | * @param createCwd 18 | */ 19 | function removeExamplePlugin(createCwd) { 20 | utils_1.logProgress("Prepare monorepo for future commands like " + chalk_1.default.underline("create-plugin") + " and " + chalk_1.default.underline("create-package") + "..."); 21 | fs_1.writeFileSync(path_1.resolve(createCwd, "plugins/wp-reactjs-starter/CHANGELOG.md"), "", { encoding: utils_1.DEFAULT_ENCODING }); 22 | fs_1.writeFileSync(path_1.resolve(createCwd, "packages/utils/CHANGELOG.md"), "", { encoding: utils_1.DEFAULT_ENCODING }); 23 | fs_1.renameSync(path_1.resolve(createCwd, "plugins/wp-reactjs-starter"), path_1.resolve(createCwd, utils_1.FOLDER_CWRA, "template")); 24 | // Copy utils package for "create-package" command 25 | var tmplPackageFolder = path_1.resolve(createCwd, utils_1.FOLDER_CWRA, "template-package"); 26 | fs_extra_1.copySync(path_1.resolve(createCwd, "packages/utils"), tmplPackageFolder); 27 | rimraf_1.default.sync(path_1.resolve(tmplPackageFolder, "lib")); 28 | rimraf_1.default.sync(path_1.resolve(tmplPackageFolder, "src")); 29 | rimraf_1.default.sync(path_1.resolve(tmplPackageFolder, "test/jest")); 30 | rimraf_1.default.sync(path_1.resolve(tmplPackageFolder, "test/phpunit")); 31 | fs_1.mkdirSync(path_1.resolve(tmplPackageFolder, "lib")); 32 | fs_1.mkdirSync(path_1.resolve(tmplPackageFolder, "src")); 33 | fs_1.mkdirSync(path_1.resolve(tmplPackageFolder, "test/jest")); 34 | fs_1.mkdirSync(path_1.resolve(tmplPackageFolder, "test/phpunit")); 35 | misc_1.modifyRootGitLabCiInclude("remove", createCwd, "wp-reactjs-starter", "plugins"); 36 | } 37 | exports.removeExamplePlugin = removeExamplePlugin; 38 | -------------------------------------------------------------------------------- /lib/create-workspace/runThirdPartyLicenses.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var execa_1 = __importDefault(require("execa")); 7 | var utils_1 = require("../utils"); 8 | /** 9 | * Generate all disclaimer files for the current workspace. 10 | * 11 | * @param createCwd 12 | */ 13 | function runThirdPartyLicenses(createCwd) { 14 | utils_1.logProgress("Start generating 3rd party disclaimer..."); 15 | execa_1.default.sync("yarn", ["--silent", "workspace:concurrently"], { 16 | cwd: createCwd, 17 | env: { 18 | WORKSPACE_COMMAND: "yarn --silent grunt composer:disclaimer --force 2>/dev/null" 19 | } 20 | }); 21 | execa_1.default.sync("yarn", ["--silent", "workspace:concurrently"], { 22 | cwd: createCwd, 23 | env: { 24 | WORKSPACE_COMMAND: "yarn --silent grunt yarn:disclaimer --force 2>/dev/null" 25 | } 26 | }); 27 | utils_1.logSuccess("Successfully generated "); 28 | } 29 | exports.runThirdPartyLicenses = runThirdPartyLicenses; 30 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | var commander_1 = require("commander"); 5 | require("./create-workspace"); 6 | require("./create-plugin"); 7 | require("./create-package"); 8 | var program = commander_1.parse(process.argv); 9 | // If no argument is passed show help 10 | if (process.argv.length < 3) { 11 | program.help(); 12 | } 13 | -------------------------------------------------------------------------------- /lib/misc/applyGitLabCi.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __spreadArrays = (this && this.__spreadArrays) || function () { 3 | for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; 4 | for (var r = Array(s), k = 0, i = 0; i < il; i++) 5 | for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) 6 | r[k] = a[j]; 7 | return r; 8 | }; 9 | var __importDefault = (this && this.__importDefault) || function (mod) { 10 | return (mod && mod.__esModule) ? mod : { "default": mod }; 11 | }; 12 | Object.defineProperty(exports, "__esModule", { value: true }); 13 | var chalk_1 = __importDefault(require("chalk")); 14 | var glob_1 = __importDefault(require("glob")); 15 | var utils_1 = require("../utils"); 16 | /** 17 | * Find GitLab CI jobs and replace them with the correct prefix (depending on constant prefix). 18 | * 19 | * This does not add the include to the root .gitlab-ci.yml! 20 | * 21 | * @param createPackageCwd 22 | * @param constantPrefix 23 | * @param prefixToReplace 24 | */ 25 | function applyGitLabCi(createPackageCwd, constantPrefix, 26 | /** 27 | * Why "utils " and "utils"? 28 | * 29 | * The package uses same name and abbreviation for folder path and package names. 30 | * 31 | * "utils " reflects all job definitions and needs always be the first replacement. 32 | */ 33 | prefixToReplace) { 34 | var jobPrefix = constantPrefix.toLowerCase(); 35 | utils_1.logProgress("Find GitLab CI jobs and prefix them with " + chalk_1.default.underline(jobPrefix) + "..."); 36 | var globFiles = function (pattern) { return glob_1.default.sync(pattern, { cwd: createPackageCwd, absolute: true }); }; 37 | var files = __spreadArrays(globFiles("devops/.gitlab/**/*.yml"), globFiles("devops/.gitlab/.gitlab-ci.yml")); 38 | utils_1.searchAndReplace(files, new RegExp(prefixToReplace, "g"), jobPrefix); 39 | } 40 | exports.applyGitLabCi = applyGitLabCi; 41 | -------------------------------------------------------------------------------- /lib/misc/applyPackageJson.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var utils_1 = require("../utils"); 7 | var chalk_1 = __importDefault(require("chalk")); 8 | var path_1 = require("path"); 9 | var fs_1 = require("fs"); 10 | /** 11 | * Update package.json with new utils version and author information. 12 | * Each package also contains a composer.json, update it too... 13 | * 14 | * It does not update the name of the package! 15 | * 16 | * @param root 17 | * @param createPackageCwd 18 | * @param input 19 | * @param updateUtilsVersion 20 | */ 21 | function applyPackageJson(root, createPackageCwd, input, updateUtilsVersion) { 22 | if (updateUtilsVersion === void 0) { updateUtilsVersion = true; } 23 | var packageJsonPath = path_1.resolve(createPackageCwd, "package.json"); 24 | var composerJsonPath = path_1.resolve(createPackageCwd, "composer.json"); 25 | utils_1.logProgress("Update " + chalk_1.default.underline(packageJsonPath) + "..."); 26 | var packageJson = fs_1.readFileSync(packageJsonPath, { encoding: utils_1.DEFAULT_ENCODING }); 27 | packageJson = packageJson.replace(/"version":\s*"([0-9.]+)"/g, '"version": "' + input.version + '"'); 28 | packageJson = packageJson.replace(/"description":\s*"([^"]+)"/g, '"description": "' + input.description + '"'); 29 | packageJson = packageJson.replace(/"author":\s*"([^"]+)"/g, '"author": "' + input.author + '"'); 30 | packageJson = packageJson.replace(/"homepage":\s*"([^"]+)"/g, '"homepage": "' + input.homepage + '"'); 31 | // Update utils version 32 | if (updateUtilsVersion) { 33 | var utilsVersion = updateUtilsVersion === true 34 | ? require(path_1.resolve(createPackageCwd, "../../packages/utils/package.json")).version 35 | : updateUtilsVersion; 36 | packageJson = packageJson.replace(new RegExp('"@' + root + '\\/utils":\\s*"\\^([0-9.]+)"', "g"), '"@' + root + '/utils": "^' + utilsVersion + '"'); 37 | } 38 | fs_1.writeFileSync(packageJsonPath, packageJson, { encoding: utils_1.DEFAULT_ENCODING }); 39 | utils_1.logSuccess("Successfully updated " + chalk_1.default.underline(packageJsonPath)); 40 | // Update composer.json 41 | utils_1.logProgress("Update " + chalk_1.default.underline(composerJsonPath) + "..."); 42 | var composerJson = fs_1.readFileSync(composerJsonPath, { encoding: utils_1.DEFAULT_ENCODING }); 43 | composerJson = composerJson.replace(/"description":\s*"([^"]+)"/g, '"description": "' + input.description + '"'); 44 | composerJson = composerJson.replace(/Matthias Günter/g, input.author); 45 | fs_1.writeFileSync(composerJsonPath, composerJson, { encoding: utils_1.DEFAULT_ENCODING }); 46 | utils_1.logSuccess("Successfully updated " + chalk_1.default.underline(composerJsonPath)); 47 | } 48 | exports.applyPackageJson = applyPackageJson; 49 | -------------------------------------------------------------------------------- /lib/misc/applyPhpNamespace.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __spreadArrays = (this && this.__spreadArrays) || function () { 3 | for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; 4 | for (var r = Array(s), k = 0, i = 0; i < il; i++) 5 | for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) 6 | r[k] = a[j]; 7 | return r; 8 | }; 9 | var __importDefault = (this && this.__importDefault) || function (mod) { 10 | return (mod && mod.__esModule) ? mod : { "default": mod }; 11 | }; 12 | Object.defineProperty(exports, "__esModule", { value: true }); 13 | var path_1 = require("path"); 14 | var fs_1 = require("fs"); 15 | var utils_1 = require("../utils"); 16 | var chalk_1 = __importDefault(require("chalk")); 17 | var glob_1 = __importDefault(require("glob")); 18 | function applyPhpNamespace(createPackageCwd, namespace, type) { 19 | // Apply the namespace 20 | utils_1.logProgress("Apply namespace " + chalk_1.default.underline(namespace) + " to all PHP files for autoloading..."); 21 | var globFiles = function (pattern) { return glob_1.default.sync(pattern, { cwd: createPackageCwd, absolute: true }); }; 22 | // Define our new package 23 | utils_1.searchAndReplace(globFiles("composer.json"), type === "utils" ? /MatthiasWeb\\\\Utils/g : /MatthiasWeb\\\\WPRJSS/g, namespace.replace(/\\/g, "\\\\")); 24 | // Search for namespaces in source and test files 25 | var phpFiles = __spreadArrays(globFiles("src/**/*.php"), globFiles("test/phpunit/**/*.php")); 26 | utils_1.searchAndReplace(phpFiles, type === "utils" ? /MatthiasWeb\\Utils/g : /MatthiasWeb\\WPRJSS/g, namespace); 27 | // Define autoloading for utils package in source and test files 28 | if (type === "plugin") { 29 | var utilsPluginReceiverFile = fs_1.readFileSync(path_1.resolve(createPackageCwd, "../../packages/utils/src/PluginReceiver.php"), { encoding: utils_1.DEFAULT_ENCODING }); 30 | var namespaceUtils = utilsPluginReceiverFile.match(/^namespace ([^;]+)/m)[1]; 31 | utils_1.searchAndReplace(globFiles("composer.lock"), /MatthiasWeb\\\\Utils/g, namespaceUtils.replace(/\\/g, "\\\\")); 32 | utils_1.searchAndReplace(phpFiles, /MatthiasWeb\\Utils/g, namespaceUtils); 33 | } 34 | utils_1.logSuccess("Successfully applied PHP namespace to package!"); 35 | } 36 | exports.applyPhpNamespace = applyPhpNamespace; 37 | -------------------------------------------------------------------------------- /lib/misc/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function __export(m) { 3 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; 4 | } 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | __export(require("./applyGitLabCi")); 7 | __export(require("./modifyRootGitLabCiInclude")); 8 | __export(require("./applyPackageJson")); 9 | __export(require("./applyPhpNamespace")); 10 | __export(require("./regenerateLaunchJson")); 11 | __export(require("./newsletterPrompt")); 12 | -------------------------------------------------------------------------------- /lib/misc/index.jsx: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function __export(m) { 3 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; 4 | } 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | __export(require("./applyGitLabCi")); 7 | __export(require("./modifyRootGitLabCiInclude")); 8 | __export(require("./applyPackageJson")); 9 | -------------------------------------------------------------------------------- /lib/misc/modifyRootGitLabCiInclude.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var chalk_1 = __importDefault(require("chalk")); 7 | var utils_1 = require("../utils"); 8 | var path_1 = require("path"); 9 | var fs_1 = require("fs"); 10 | /** 11 | * Modify the anchor content of the root .gitlab-ci.yml file to add or 12 | * remove a plugin's or package CI configuration. 13 | * 14 | * @param action 15 | * @param cwd 16 | * @param slug 17 | */ 18 | function modifyRootGitLabCiInclude(action, cwd, slug, type) { 19 | utils_1.logProgress("Modify root .gitlab-ci.yml and " + action + " include " + chalk_1.default.underline(slug) + "..."); 20 | var path = path_1.resolve(cwd, ".gitlab-ci.yml"); 21 | var content = fs_1.readFileSync(path, { encoding: utils_1.DEFAULT_ENCODING }); 22 | content = content.replace(/# create-wp-react-app -->\n(.*)\n(\s*)# <-- create-wp-react-app/gms, function (match, inner, spaceBefore) { 23 | var newLines = inner.split("\n").filter(Boolean); 24 | if (action === "remove") { 25 | newLines = newLines.filter(function (line) { return line.indexOf("- /" + type + "/" + slug + "/") === -1; }); 26 | } 27 | else { 28 | newLines.push(spaceBefore + "- /" + type + "/" + slug + "/devops/.gitlab/.gitlab-ci.yml"); 29 | } 30 | return "# create-wp-react-app -->\n" + newLines.join("\n") + "\n" + spaceBefore + "# <-- create-wp-react-app"; 31 | }); 32 | fs_1.writeFileSync(path, content, { encoding: utils_1.DEFAULT_ENCODING }); 33 | utils_1.logSuccess("Successfully updated " + path); 34 | } 35 | exports.modifyRootGitLabCiInclude = modifyRootGitLabCiInclude; 36 | -------------------------------------------------------------------------------- /lib/misc/newsletterPrompt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (_) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | var __importDefault = (this && this.__importDefault) || function (mod) { 39 | return (mod && mod.__esModule) ? mod : { "default": mod }; 40 | }; 41 | Object.defineProperty(exports, "__esModule", { value: true }); 42 | var chalk_1 = __importDefault(require("chalk")); 43 | var inquirer_1 = require("inquirer"); 44 | var utils_1 = require("../utils"); 45 | var terminal_link_1 = __importDefault(require("terminal-link")); 46 | var axios_1 = __importDefault(require("axios")); 47 | function newsletterPrompt(condition) { 48 | return __awaiter(this, void 0, void 0, function () { 49 | var email, _a, e_1; 50 | return __generator(this, function (_b) { 51 | switch (_b.label) { 52 | case 0: 53 | _a = condition; 54 | if (!_a) return [3 /*break*/, 2]; 55 | return [4 /*yield*/, inquirer_1.prompt([ 56 | { 57 | name: "email", 58 | message: "You will \u2665 create-wp-react-app and wp-react-starter! Would you like to be informed about updates via e-mail (you agree " + terminal_link_1.default("devowl.io privacy policy", "https://devowl.io/privacy-policy/") + ")? Enter your e-mail:", 59 | type: "input", 60 | validate: function (value) { 61 | // eslint-disable-next-line no-useless-escape 62 | return !value || 63 | /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(value) 64 | ? true 65 | : "This is not an valid email."; 66 | } 67 | } 68 | ])]; 69 | case 1: 70 | _a = (_b.sent()); 71 | _b.label = 2; 72 | case 2: 73 | email = (_a).email; 74 | if (!email) return [3 /*break*/, 6]; 75 | utils_1.logProgress("Registering on newsletter..."); 76 | _b.label = 3; 77 | case 3: 78 | _b.trys.push([3, 5, , 6]); 79 | return [4 /*yield*/, axios_1.default.post("https://devowl.io/wp-json/devowl-site/v1/plugin-activation-newsletter", { 80 | email: email, 81 | referer: "localhost", 82 | slug: "create-wp-react-app" 83 | })]; 84 | case 4: 85 | _b.sent(); 86 | utils_1.logSuccess("Successfully registered " + chalk_1.default.underline(email) + "!"); 87 | return [3 /*break*/, 6]; 88 | case 5: 89 | e_1 = _b.sent(); 90 | utils_1.logError("Error while newsletter registration, skipping (" + e_1 + ")..."); 91 | return [3 /*break*/, 6]; 92 | case 6: return [2 /*return*/]; 93 | } 94 | }); 95 | }); 96 | } 97 | exports.newsletterPrompt = newsletterPrompt; 98 | -------------------------------------------------------------------------------- /lib/misc/regenerateLaunchJson.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var chalk_1 = __importDefault(require("chalk")); 7 | var execa_1 = __importDefault(require("execa")); 8 | var utils_1 = require("../utils"); 9 | /** 10 | * Regenerate launch.json file. 11 | * 12 | * @param createCwd 13 | */ 14 | function regenerateLaunchJson(createCwd) { 15 | utils_1.logProgress("Regenerate " + chalk_1.default.underline("launch.json") + " for debug purposes..."); 16 | execa_1.default.sync("yarn", ["debug:php:generate"], { cwd: createCwd, stdio: "inherit" }); 17 | } 18 | exports.regenerateLaunchJson = regenerateLaunchJson; 19 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | var execa_1 = __importDefault(require("execa")); 7 | var chalk_1 = __importDefault(require("chalk")); 8 | var path_1 = require("path"); 9 | var fs_1 = require("fs"); 10 | var url_1 = require("url"); 11 | var fs_extra_1 = require("fs-extra"); 12 | var FOLDER_CWRA = "common/create-wp-react-app"; 13 | exports.FOLDER_CWRA = FOLDER_CWRA; 14 | var DEFAULT_ENCODING = "UTF-8"; 15 | exports.DEFAULT_ENCODING = DEFAULT_ENCODING; 16 | var logProgress = function (text) { return console.log(chalk_1.default.black.bgCyan(text)); }; 17 | exports.logProgress = logProgress; 18 | var logSuccess = function (text) { return console.log(chalk_1.default.black.green(text)); }; 19 | exports.logSuccess = logSuccess; 20 | var logError = function (text) { return console.log(chalk_1.default.red(text)); }; 21 | exports.logError = logError; 22 | /** 23 | * Similar to runThirdPartyLicenses but only for one package. 24 | * 25 | * @param cwd 26 | */ 27 | function runThirdPartyLicenseForPackage(cwd) { 28 | logProgress("Generate 3rd party licenses in " + chalk_1.default.underline(cwd) + "..."); 29 | execa_1.default.sync("yarn", ["grunt", "composer:disclaimer"], { cwd: cwd }); 30 | execa_1.default.sync("yarn", ["grunt", "yarn:disclaimer"], { cwd: cwd }); 31 | logSuccess("Successfully generated 3rd party licenses"); 32 | } 33 | exports.runThirdPartyLicenseForPackage = runThirdPartyLicenseForPackage; 34 | /** 35 | * Generate a LICENSE file for a given package.json. 36 | * 37 | * @param cwd 38 | * @param author 39 | * @param description 40 | */ 41 | function generateLicenseFile(cwd, author, description) { 42 | var licensePath = path_1.resolve(cwd, "LICENSE"); 43 | logProgress("Generate LICENSE file in " + chalk_1.default.underline(licensePath) + "..."); 44 | var licenseFile = fs_1.readFileSync(licensePath, { encoding: DEFAULT_ENCODING }) 45 | .split("\n") 46 | .slice(2) 47 | .join("\n"); 48 | var year = new Date().getFullYear(); 49 | var header = description + "\nCopyright (C) " + year + " " + author + "\n"; 50 | fs_1.writeFileSync(licensePath, header + licenseFile); 51 | logSuccess("Successfully generated LICENSE file"); 52 | return licensePath; 53 | } 54 | exports.generateLicenseFile = generateLicenseFile; 55 | /** 56 | * Get a valid workspace package.json content. 57 | * 58 | * @param cwd 59 | * @returns package.json content of the root 60 | * @throws 61 | */ 62 | function getValidWorkspace(cwd) { 63 | var json = require(path_1.join(cwd, "package.json")); // eslint-disable-line @typescript-eslint/no-var-requires 64 | logSuccess("Successfully found " + chalk_1.default.underline(json.name) + " as root project!"); 65 | if (!json.private) { 66 | throw new Error("This project is not private. Yarn root workspaces must be private!"); 67 | } 68 | if (!json.workspaces) { 69 | throw new Error("This project has no workspaces defined."); 70 | } 71 | if (JSON.stringify(json.workspaces).indexOf("plugins/*") === -1) { 72 | throw new Error("This project has no plugins/* workspaces defined."); 73 | } 74 | return json; 75 | } 76 | exports.getValidWorkspace = getValidWorkspace; 77 | /** 78 | * Get CLI arguments if SKIP_PROMPT is set so the prompt is skipped. 79 | * This is for development purposes only! 80 | * 81 | * @param type 82 | * @private 83 | */ 84 | function getInternalExampleArgs(type) { 85 | if (process.env.SKIP_PROMPT) { 86 | switch (type) { 87 | case "workspace": 88 | return { 89 | checkout: "feat/multipackage", 90 | portPma: 9099, 91 | portWp: 9098, 92 | workspace: "my-awesomeness" 93 | }; 94 | case "plugin": 95 | return { 96 | author: "Jon Smith", 97 | authorUri: "https://example.com", 98 | constantPrefix: "AWEONE", 99 | dbPrefix: "aweone", 100 | minPhp: "7.0.0", 101 | minWp: "5.2.0", 102 | namespace: "JonSmith\\AwesomeOne", 103 | optPrefix: "awe", 104 | pluginDesc: "This is an awesome plugin #1!", 105 | pluginName: "Awesome One", 106 | pluginUri: "https://awesome-one.example.com", 107 | pluginVersion: "1.0.0", 108 | slug: "awesome-one" 109 | }; 110 | case "package": 111 | return { 112 | abbreviation: "mbp", 113 | author: "Jon Smith", 114 | namespace: "JonSmith\\MyBestPackage", 115 | packageDesc: "This is an awesome package?!", 116 | packageName: "my-best-package", 117 | packageUri: "https://my-best-package.example.com" 118 | }; 119 | } 120 | } 121 | return null; 122 | } 123 | exports.getInternalExampleArgs = getInternalExampleArgs; 124 | /** 125 | * Get an option from a command by long definition. 126 | * 127 | * @param c 128 | * @param long 129 | */ 130 | function getCommandOption(c, long) { 131 | return c.options.filter(function (o) { return o.long === long; })[0]; 132 | } 133 | exports.getCommandOption = getCommandOption; 134 | /** 135 | * Used in prompts so you do not have to duplicate the question strings 136 | * and take it directly from the commander option description. 137 | * 138 | * @param c 139 | * @param long 140 | */ 141 | function getCommandDescriptionForPrompt(c, long) { 142 | return getCommandOption(c, long).description; 143 | } 144 | exports.getCommandDescriptionForPrompt = getCommandDescriptionForPrompt; 145 | /** 146 | * Search and replace file content and write it back with success message. 147 | * 148 | * @param files Absolute pathes 149 | * @param search 150 | * @param replace 151 | */ 152 | function searchAndReplace(files, search, replace) { 153 | var wroteHeader = false; 154 | files.forEach(function (file) { 155 | if (file.indexOf(FOLDER_CWRA) === -1) { 156 | var i_1 = 0; 157 | var newContent = fs_1.readFileSync(file, { encoding: DEFAULT_ENCODING }).replace(search, function () { 158 | i_1++; 159 | return replace; 160 | }); 161 | fs_1.writeFileSync(file, newContent, { encoding: DEFAULT_ENCODING }); 162 | if (i_1 > 0) { 163 | if (!wroteHeader) { 164 | logSuccess("Search (" + search + ") & Replace (" + replace + "):"); 165 | wroteHeader = true; 166 | } 167 | logSuccess("\u251C\u2500\u2500 " + chalk_1.default.underline(file) + " (" + i_1 + " times)"); 168 | } 169 | } 170 | }); 171 | } 172 | exports.searchAndReplace = searchAndReplace; 173 | /** 174 | * Return an error message when the given input is empty. 175 | * 176 | * @param value 177 | * @returns 178 | */ 179 | function inquirerRequiredValidate(value) { 180 | if (!value) { 181 | return "This prompt may not be empty!"; 182 | } 183 | return true; 184 | } 185 | exports.inquirerRequiredValidate = inquirerRequiredValidate; 186 | /** 187 | * Adjust the cases by keys in a given object. 188 | * 189 | * @param object 190 | * @param upper 191 | * @param lower 192 | */ 193 | function caseAll(object, upper, lower) { 194 | upper.forEach(function (i) { return (object[i] = object[i].toUpperCase()); }); 195 | lower.forEach(function (i) { return (object[i] = object[i].toLowerCase()); }); 196 | return object; 197 | } 198 | exports.caseAll = caseAll; 199 | /** 200 | * Get a git config by parameter. 201 | * 202 | * @param param 203 | * @returns 204 | */ 205 | function getGitConfig(param) { 206 | try { 207 | return execa_1.default.sync("git", ["config", "--get", param]).stdout; 208 | } 209 | catch (e) { 210 | return ""; 211 | } 212 | } 213 | exports.getGitConfig = getGitConfig; 214 | /** 215 | * Convert a slug like "my-plugin" to "myPlugin". 216 | * 217 | * @param slug 218 | * @param firstUc 219 | * @returns 220 | */ 221 | function slugCamelCase(slug, firstUc) { 222 | if (firstUc === void 0) { firstUc = false; } 223 | var result = slug.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); }); 224 | return firstUc ? result.charAt(0).toUpperCase() + result.slice(1) : result; 225 | } 226 | exports.slugCamelCase = slugCamelCase; 227 | /** 228 | * Checks if a given version is a valid semver. 229 | * 230 | * @param version 231 | * @param errorAsString 232 | * @returns 233 | * @see https://github.com/semver/semver/issues/232 234 | */ 235 | function isValidSemver(version, errorAsString) { 236 | if (errorAsString === void 0) { errorAsString = false; } 237 | var valid = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$/.test(version); 238 | return valid ? true : errorAsString ? "This is not a valid version." : false; 239 | } 240 | exports.isValidSemver = isValidSemver; 241 | /** 242 | * Checks if a given name is a valid PHP namespace. 243 | * 244 | * @param name 245 | * @returns 246 | */ 247 | function isValidPhpNamespace(name) { 248 | return /^[^\\0-9][A-Za-z0-9_\\]+$/.test(name); 249 | } 250 | exports.isValidPhpNamespace = isValidPhpNamespace; 251 | /** 252 | * Checks if the given url is valid. 253 | * 254 | * @param url 255 | * @param errorAsString 256 | * @returns 257 | */ 258 | function isValidUrl(url, errorAsString) { 259 | if (errorAsString === void 0) { errorAsString = false; } 260 | try { 261 | new url_1.URL(url); 262 | return true; 263 | } 264 | catch (e) { 265 | return errorAsString ? "This is not a valid URL" : false; 266 | } 267 | } 268 | exports.isValidUrl = isValidUrl; 269 | /** 270 | * Check for valid workspace and exit if not found. 271 | * 272 | * @param createWorkspaceCwd 273 | * @returns 274 | */ 275 | function checkValidWorkspace(createWorkspaceCwd, outputHelp) { 276 | // Get the root package 277 | try { 278 | var root = getValidWorkspace(createWorkspaceCwd); 279 | if (!fs_extra_1.existsSync(path_1.resolve(createWorkspaceCwd, FOLDER_CWRA, "template"))) { 280 | throw new Error("The template folder in common/create-wp-react-app could not be found!"); 281 | } 282 | return root; 283 | } 284 | catch (e) { 285 | logError(e.toString()); 286 | logError("You are not using the command inside a folder which was created with " + chalk_1.default.underline("create-wp-react-app create-workspace") + ". Navigate to that folder or use the " + chalk_1.default.underline("--cwd") + " argument."); 287 | outputHelp.help(); 288 | } 289 | } 290 | exports.checkValidWorkspace = checkValidWorkspace; 291 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-wp-react-app", 3 | "version": "2.1.2", 4 | "description": "Create React WordPress plugin with no build configuration.", 5 | "scripts": { 6 | "build": "tsc", 7 | "dev": "tsc -w", 8 | "lint": "eslint \"src/**/*.ts\"" 9 | }, 10 | "keywords": [ 11 | "wp", 12 | "react", 13 | "zero-configuration", 14 | "build-tools" 15 | ], 16 | "bin": { 17 | "create-wp-react-app": "lib/index.js" 18 | }, 19 | "homepage": "https://devowl.io/wp-react-starter/", 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/devowlio/create-wp-react-app.git" 23 | }, 24 | "bugs": { 25 | "url": "https://github.com/devowlio/create-wp-react-app/issues" 26 | }, 27 | "author": { 28 | "name": "Matthias Günter", 29 | "email": "matthias.guenter@devowl.io", 30 | "url": "https://devowl.io/" 31 | }, 32 | "contributors": [ 33 | { 34 | "name": "devowl.io GmbH", 35 | "email": "mail@devowl.io" 36 | } 37 | ], 38 | "license": "MIT", 39 | "husky": { 40 | "hooks": { 41 | "pre-commit": "pretty-quick --staged", 42 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 43 | } 44 | }, 45 | "commitlint": { 46 | "extends": [ 47 | "@commitlint/config-conventional" 48 | ], 49 | "rules": { 50 | "header-max-length": [ 51 | 2, 52 | "always", 53 | 140 54 | ] 55 | } 56 | }, 57 | "prettier": { 58 | "arrowParens": "always", 59 | "printWidth": 120, 60 | "useTabs": false, 61 | "tabWidth": 4, 62 | "endOfLine": "lf" 63 | }, 64 | "dependencies": { 65 | "axios": "^0.19.2", 66 | "chalk": "^3.0.0", 67 | "commander": "^4.0.1", 68 | "execa": "^4.0.0", 69 | "fs-extra": "^8.1.0", 70 | "gitlab": "^14.2.2", 71 | "glob": "^7.1.6", 72 | "inquirer": "^7.0.0", 73 | "listr": "^0.14.2", 74 | "listr-input": "^0.2.0", 75 | "lodash": "^4.17.15", 76 | "rimraf": "^3.0.0", 77 | "slugify": "^1.3.6", 78 | "terminal-link": "^2.0.0" 79 | }, 80 | "devDependencies": { 81 | "@commitlint/cli": "^8.2.0", 82 | "@commitlint/config-conventional": "^8.2.0", 83 | "@samverschueren/stream-to-observable": "^0.3.0", 84 | "@types/fs-extra": "^8.0.1", 85 | "@types/inquirer": "^6.5.0", 86 | "@types/rimraf": "^2.0.3", 87 | "@typescript-eslint/eslint-plugin": "^2.9.0", 88 | "@typescript-eslint/parser": "^2.9.0", 89 | "eslint": "^6.7.1", 90 | "eslint-config-prettier": "^6.7.0", 91 | "eslint-plugin-prettier": "^3.1.1", 92 | "husky": "^4.2.1", 93 | "prettier": "^1.19.1", 94 | "pretty-quick": "^2.0.1", 95 | "ts-node": "^8.5.2", 96 | "typescript": "^3.7.2", 97 | "zen-observable": "^0.8.14" 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/create-package/applyName.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import { CreatePackageOpts } from "./program"; 3 | import { readFileSync } from "fs-extra"; 4 | import { resolve } from "path"; 5 | import { DEFAULT_ENCODING, logProgress, logSuccess, searchAndReplace } from "../utils"; 6 | import glob from "glob"; 7 | 8 | /** 9 | * Apply a package name to a given cwd. The scope name is automatically read from 10 | * the parent root package.json#name variable. 11 | * 12 | * @param createPackageCwd 13 | * @param name 14 | */ 15 | function applyName(createPackageCwd: string, name: CreatePackageOpts["packageName"]) { 16 | // Generate scoped package name 17 | const scopeName = 18 | JSON.parse(readFileSync(resolve(createPackageCwd, "../../package.json"), { encoding: DEFAULT_ENCODING })).name + 19 | "/" + 20 | name; 21 | 22 | logProgress(`Apply package name ${chalk.underline(scopeName)} to ${chalk.underline(createPackageCwd)}`); 23 | const globFiles = (pattern: string) => glob.sync(pattern, { cwd: createPackageCwd, absolute: true }); 24 | const files = [...globFiles("{package,composer}.json"), ...globFiles("README.md")]; 25 | searchAndReplace(files, /wp-reactjs-multi-starter\/utils/g, scopeName); 26 | logSuccess(`Successfully applied name to package!`); 27 | 28 | return scopeName; 29 | } 30 | 31 | export { applyName }; 32 | -------------------------------------------------------------------------------- /src/create-package/copyTemplates.ts: -------------------------------------------------------------------------------- 1 | import rimraf from "rimraf"; 2 | import chalk from "chalk"; 3 | import { resolve } from "path"; 4 | import { logProgress, FOLDER_CWRA, logSuccess } from "../utils"; 5 | import { copySync } from "fs-extra"; 6 | 7 | /** 8 | * Copy templates to the given path and read template files. 9 | * 10 | * @param createPackageCwd 11 | */ 12 | function copyTemplates(createPackageCwd: string) { 13 | logProgress("Create new package from template..."); 14 | const templateCwd = resolve(createPackageCwd, "../..", FOLDER_CWRA, "template-package"); 15 | copySync(templateCwd, createPackageCwd); 16 | 17 | // Delete language folder 18 | rimraf.sync(resolve(createPackageCwd, "languages")); 19 | 20 | logSuccess(`Successfully created package folder ${chalk.underline(createPackageCwd)}`); 21 | } 22 | 23 | export { copyTemplates }; 24 | -------------------------------------------------------------------------------- /src/create-package/createExampleFiles.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import { logProgress, logSuccess } from "../utils"; 3 | import { CreatePackageOpts } from "./program"; 4 | import { writeFileSync, mkdirSync } from "fs-extra"; 5 | import { resolve } from "path"; 6 | 7 | /** 8 | * Create an example MyClass.php file and an example .tsx file. 9 | * 10 | * @param createPackageCwd 11 | * @param packageName 12 | * @param namespace 13 | */ 14 | function createExampleFiles( 15 | createPackageCwd: string, 16 | packageName: CreatePackageOpts["packageName"], 17 | namespace: CreatePackageOpts["namespace"] 18 | ) { 19 | logProgress(`Create example PHP and TSX file in ${chalk.underline(createPackageCwd)}...`); 20 | const indexTsx = `import "setimmediate"; // Polyfill for yielding 21 | 22 | function sayHello() { 23 | console.log("Hello from ${packageName}"); 24 | } 25 | 26 | export { sayHello }; 27 | `; 28 | 29 | const myClassPhp = `", "The path to the workspace where the package should be created", process.cwd()) 17 | .option("--package-name ", "How should your package be named?") 18 | .option("--package-desc ", "Give a one-line package description?") 19 | .option("--author ", "Who is the package author?") 20 | .option("--package-uri ", "What's your package' homepage URL?") 21 | .option("--namespace ", "What's the PHP Namespace (e. g 'MatthiasWeb\\WPRJSS')?") 22 | .option("--abbreviation ", "What's a short abbreviation for this package?") 23 | .action((args) => createPackagePrompt(args)); 24 | 25 | export { CreatePackageOpts, createPackageCommand }; 26 | -------------------------------------------------------------------------------- /src/create-package/prompt.ts: -------------------------------------------------------------------------------- 1 | import { prompt } from "inquirer"; 2 | import { CreatePackageOpts, createPackageCommand, createPackageExecute } from "./"; 3 | import { 4 | logError, 5 | caseAll, 6 | checkValidWorkspace, 7 | getCommandDescriptionForPrompt, 8 | inquirerRequiredValidate, 9 | getGitConfig, 10 | isValidPhpNamespace, 11 | slugCamelCase, 12 | isValidUrl, 13 | getInternalExampleArgs 14 | } from "../utils"; 15 | import { resolve } from "path"; 16 | import slugify from "slugify"; 17 | import { newsletterPrompt } from "../misc"; 18 | 19 | /** 20 | * Prompt for CLI arguments which are not passed. 21 | * 22 | * @param opts 23 | */ 24 | async function createPackagePrompt({ 25 | cwd, 26 | packageName, 27 | packageDesc, 28 | author, 29 | packageUri, 30 | namespace, 31 | abbreviation 32 | }: Partial) { 33 | const createWorkspaceCwd = cwd ? resolve(cwd) : process.cwd(); 34 | const root = checkValidWorkspace(createWorkspaceCwd, createPackageCommand); 35 | 36 | const gitName = getGitConfig("user.name"); 37 | const mockData = getInternalExampleArgs("package"); 38 | 39 | const answers = 40 | (mockData as any) || 41 | (await prompt( 42 | [ 43 | !packageName && { 44 | name: "packageName", 45 | message: getCommandDescriptionForPrompt(createPackageCommand, "--package-name"), 46 | type: "input", 47 | validate: (value: string) => 48 | /^[A-Za-z0-9-_]+$/.test(value) ? true : "Your package slug should only contain [A-Za-z0-9-_]." 49 | }, 50 | !packageDesc && { 51 | name: "packageDesc", 52 | message: getCommandDescriptionForPrompt(createPackageCommand, "--package-desc"), 53 | type: "input" 54 | }, 55 | !author && { 56 | name: "author", 57 | message: getCommandDescriptionForPrompt(createPackageCommand, "--author"), 58 | type: "input", 59 | validate: inquirerRequiredValidate, 60 | default: gitName 61 | }, 62 | !packageUri && { 63 | name: "packageUri", 64 | message: getCommandDescriptionForPrompt(createPackageCommand, "--package-uri"), 65 | type: "input", 66 | validate: (value: string) => (value ? isValidUrl(value, true) : true) 67 | }, 68 | !namespace && { 69 | name: "namespace", 70 | message: getCommandDescriptionForPrompt(createPackageCommand, "--namespace"), 71 | type: "input", 72 | validate: (value: string) => { 73 | const required = inquirerRequiredValidate(value); 74 | if (required !== true) { 75 | return required; 76 | } 77 | return isValidPhpNamespace(value) ? true : "This is not a valid PHP namespace."; 78 | }, 79 | default: (dAnswers: any) => { 80 | const useThis = (dAnswers.author || author) as string; 81 | const useSlug = (dAnswers.packageName || packageName) as string; 82 | return useThis && useSlug 83 | ? slugCamelCase( 84 | slugify(useThis, { 85 | lower: true 86 | }), 87 | true 88 | ) + 89 | "\\" + 90 | slugCamelCase(useSlug, true) 91 | : undefined; 92 | } 93 | }, 94 | !abbreviation && { 95 | name: "abbreviation", 96 | message: getCommandDescriptionForPrompt(createPackageCommand, "--abbreviation"), 97 | type: "input", 98 | validate: (value: string) => { 99 | const required = inquirerRequiredValidate(value); 100 | if (required !== true) { 101 | return required; 102 | } 103 | return /^[A-Za-z_]+$/.test(value) 104 | ? true 105 | : "This is not a valid abbreviation (allowed: [A-Za-z_])."; 106 | }, 107 | default: (dAnswers: any) => { 108 | const result = ((dAnswers.packageName || packageName) as string).replace(/-/g, "_"); 109 | const availableFirstLetters = result.match(/_(.)/g); 110 | if (availableFirstLetters && availableFirstLetters.length > 1) { 111 | return result.charAt(0) + availableFirstLetters.map((o) => o.slice(1)).join(""); 112 | } 113 | return result; 114 | } 115 | } 116 | ].filter(Boolean) 117 | )); 118 | 119 | // If there is no package name given via CLI also ask for email marketing 120 | await newsletterPrompt(!(mockData as any) && !packageName); 121 | 122 | try { 123 | let parsed = { 124 | cwd, 125 | packageName, 126 | packageDesc, 127 | author, 128 | packageUri, 129 | namespace, 130 | abbreviation, 131 | ...answers 132 | }; 133 | parsed.namespace = parsed.namespace.replace(/\\\\/g, "\\"); 134 | parsed = caseAll(parsed, [], ["abbreviation", "packageName", "packageUri"]); 135 | 136 | await createPackageExecute(root, parsed); 137 | } catch (e) { 138 | logError(e.toString()); 139 | } 140 | } 141 | 142 | export { createPackagePrompt }; 143 | -------------------------------------------------------------------------------- /src/create-plugin/applyPhpConstants.ts: -------------------------------------------------------------------------------- 1 | import { CreatePluginOpts } from "./program"; 2 | import glob from "glob"; 3 | import { logProgress, logSuccess, searchAndReplace } from "../utils"; 4 | import { applyPromptsToTemplates } from "./"; 5 | 6 | /** 7 | * Apply PHP constants to the available *.php files. Also adjust UtilsProvider trait. 8 | * 9 | * @param createPluginCwd 10 | * @param appliedTemplates 11 | * @param constantPrefix 12 | */ 13 | function applyPhpConstants( 14 | createPluginCwd: string, 15 | appliedTemplates: ReturnType, 16 | constantPrefix: CreatePluginOpts["constantPrefix"] 17 | ) { 18 | logProgress("Get and apply all your PHP constants to the *.php files..."); 19 | // Find constants 20 | let m; 21 | const regex = /define\('([^']+)/g; 22 | const constantList: string[] = []; 23 | 24 | // tslint:disable-next-line: no-conditional-assignment 25 | while ((m = regex.exec(appliedTemplates.indexPhpContent)) !== null) { 26 | if (m.index === regex.lastIndex) { 27 | regex.lastIndex++; 28 | } 29 | m.forEach((match, groupIndex) => { 30 | if (groupIndex === 1) { 31 | constantList.push(match); 32 | } 33 | }); 34 | } 35 | 36 | // Search & Replace constants 37 | const phpFiles = glob.sync("src/**/*.php", { cwd: createPluginCwd, absolute: true }); 38 | constantList.forEach((constant) => 39 | searchAndReplace(phpFiles, new RegExp("WPRJSS" + constant.slice(constantPrefix.length), "g"), constant) 40 | ); 41 | searchAndReplace( 42 | glob.sync("src/inc/base/UtilsProvider.php", { cwd: createPluginCwd, absolute: true }), 43 | /'WPRJSS'/g, 44 | `'${constantPrefix}'` 45 | ); 46 | logSuccess( 47 | `Successfully applied the following constants which you can use now - read more about them in WP ReactJS Starter documentation:\n - ${constantList.join( 48 | "\n - " 49 | )}` 50 | ); 51 | } 52 | 53 | export { applyPhpConstants }; 54 | -------------------------------------------------------------------------------- /src/create-plugin/applyPhpFunctions.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import glob from "glob"; 3 | import { logProgress, searchAndReplace } from "../utils"; 4 | 5 | /** 6 | * Find PHP functions starting with wprjss_skip and replace them with the 7 | * correct prefix (depending on constant prefix). 8 | * 9 | * @param createPluginCwd 10 | * @param constantPrefix 11 | */ 12 | function applyPhpFunctions(createPluginCwd: string, constantPrefix: string) { 13 | const functionPrefix = constantPrefix.toLowerCase(); 14 | logProgress(`Find PHP functions and replace with ${chalk.underline(functionPrefix)} prefix...`); 15 | const phpFiles = glob.sync("src/**/*.php", { cwd: createPluginCwd, absolute: true }); 16 | searchAndReplace(phpFiles, /wprjss_skip/g, functionPrefix + "_skip"); 17 | } 18 | 19 | export { applyPhpFunctions }; 20 | -------------------------------------------------------------------------------- /src/create-plugin/applyPromptsToTemplates.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import { resolve } from "path"; 3 | import { CreatePluginOpts } from "./program"; 4 | import { logSuccess, DEFAULT_ENCODING } from "../utils"; 5 | import { writeFileSync } from "fs"; 6 | import { copyTemplates } from "./"; 7 | 8 | /** 9 | * Create template files with the given prompt values. 10 | * 11 | * @param rootName 12 | * @param createPluginCwd 13 | * @param templates 14 | * @param input 15 | * @returns 16 | */ 17 | function applyPromptsToTemplates( 18 | rootName: string, 19 | createPluginCwd: string, 20 | templates: ReturnType, 21 | input: CreatePluginOpts 22 | ) { 23 | const applyTemplate = (tmpl: string) => { 24 | let mod = tmpl; 25 | Object.entries(input).forEach(([key, value]) => { 26 | if (key === "namespace") { 27 | value = value.replace(/\\/g, "\\\\"); 28 | } 29 | mod = mod.replace(new RegExp("\\$\\{" + key + "\\}", "g"), value); 30 | }); 31 | mod = mod.replace(new RegExp("\\$\\{rootName\\}", "g"), rootName); 32 | return mod; 33 | }; 34 | const indexPhpFile = resolve(createPluginCwd, "src/index.php"); 35 | const readmeTxtFile = resolve(createPluginCwd, "wordpress.org/README.wporg.txt"); 36 | const indexPhpContent = applyTemplate(templates["index.php"]); 37 | const readmeTxtContent = applyTemplate(templates["readme.wporg.txt"]); 38 | writeFileSync(indexPhpFile, indexPhpContent, { encoding: DEFAULT_ENCODING }); 39 | logSuccess(`Successfully created main plugin file ${chalk.underline(indexPhpFile)}`); 40 | writeFileSync(readmeTxtFile, readmeTxtContent, { encoding: DEFAULT_ENCODING }); 41 | logSuccess(`Successfully created readme file for wordpress.org ${chalk.underline(readmeTxtFile)}`); 42 | return { 43 | indexPhpFile, 44 | readmeTxtFile, 45 | indexPhpContent, 46 | readmeTxtContent 47 | }; 48 | } 49 | 50 | export { applyPromptsToTemplates }; 51 | -------------------------------------------------------------------------------- /src/create-plugin/applySlug.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import glob from "glob"; 3 | import { logProgress, searchAndReplace } from "../utils"; 4 | 5 | /** 6 | * Apply the slug to different files where needed. Also apply the main root slug 7 | * in the plugin so the utils package for example can be correctly linked and resolved. 8 | * 9 | * @param workspace 10 | * @param createPluginCwd 11 | * @param slug 12 | */ 13 | function applySlug(workspace: string, createPluginCwd: string, slug: string) { 14 | logProgress(`Find various code places and replace the slug with ${chalk.underline(slug)}...`); 15 | const globFiles = (pattern: string) => glob.sync(pattern, { cwd: createPluginCwd, absolute: true }); 16 | const files = [ 17 | ...globFiles("devops/**/*.{yml,sh}"), 18 | ...globFiles("devops/.gitlab/**/*.yml"), 19 | ...globFiles("devops/.gitlab/.gitlab-ci.yml"), 20 | ...globFiles("package.json"), 21 | ...globFiles("src/**/*.php") 22 | ]; 23 | searchAndReplace(files, /wp-reactjs-starter/g, slug); 24 | 25 | // Get root name and replace wp-reactjs-multi-starter 26 | logProgress(`Find various code places and replace the root package name with ${chalk.underline(workspace)}...`); 27 | const multiStarterFiles = [ 28 | ...globFiles("src/public/ts/**/*.tsx"), 29 | ...globFiles("test/jest/**/*.tsx"), 30 | ...globFiles("{composer,package}.json"), 31 | ...globFiles("composer.lock"), 32 | ...globFiles("src/index.php") 33 | ]; 34 | searchAndReplace(multiStarterFiles, /wp-reactjs-multi-starter/g, workspace); 35 | } 36 | 37 | export { applySlug }; 38 | -------------------------------------------------------------------------------- /src/create-plugin/copyTemplates.ts: -------------------------------------------------------------------------------- 1 | import rimraf from "rimraf"; 2 | import chalk from "chalk"; 3 | import { resolve } from "path"; 4 | import { logProgress, FOLDER_CWRA, logSuccess, DEFAULT_ENCODING } from "../utils"; 5 | import { readFileSync } from "fs"; 6 | import { copySync } from "fs-extra"; 7 | 8 | /** 9 | * Copy templates to the given path and read template files. 10 | * 11 | * @param createPluginCwd 12 | * @returns 13 | */ 14 | function copyTemplates(createPluginCwd: string) { 15 | logProgress("Create new plugin from template..."); 16 | const templateCwd = resolve(createPluginCwd, "../..", FOLDER_CWRA, "template"); 17 | copySync(templateCwd, createPluginCwd); 18 | const templates = { 19 | "index.php": readFileSync(resolve(templateCwd, "../grunt-index-php.tmpl"), { encoding: DEFAULT_ENCODING }), 20 | "readme.wporg.txt": readFileSync(resolve(templateCwd, "../grunt-readme-txt.tmpl"), { 21 | encoding: DEFAULT_ENCODING 22 | }) 23 | }; 24 | 25 | // Delete language folders 26 | rimraf.sync(resolve(createPluginCwd, "src/languages")); 27 | rimraf.sync(resolve(createPluginCwd, "src/public/languages")); 28 | 29 | logSuccess(`Successfully created plugin folder ${chalk.underline(createPluginCwd)}`); 30 | return templates; 31 | } 32 | 33 | export { copyTemplates }; 34 | -------------------------------------------------------------------------------- /src/create-plugin/execute.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from "path"; 2 | import { CreatePluginOpts } from "./program"; 3 | import { existsSync } from "fs"; 4 | import { 5 | copyTemplates, 6 | applyPromptsToTemplates, 7 | applyPhpConstants, 8 | applyPhpFunctions, 9 | applySlug, 10 | preInstallationBuilds 11 | } from "./"; 12 | import { logSuccess, logProgress, runThirdPartyLicenseForPackage, generateLicenseFile } from "../utils"; 13 | import chalk from "chalk"; 14 | import execa from "execa"; 15 | import { 16 | applyGitLabCi, 17 | modifyRootGitLabCiInclude, 18 | applyPackageJson, 19 | regenerateLaunchJson, 20 | applyPhpNamespace 21 | } from "../misc"; 22 | 23 | /** 24 | * Generate a new plugin from the template. All validations are done in createPluginPrompt. 25 | * 26 | * @param root 27 | * @param input 28 | * @param fromWorkspace 29 | * @returns 30 | * @throws 31 | */ 32 | async function createPluginExecute(root: any, input: CreatePluginOpts, fromWorkspace = false) { 33 | const createPluginCwd = resolve(input.cwd, "plugins", input.slug); 34 | 35 | // Strictly do not override an existing plugin!! 36 | if (existsSync(createPluginCwd)) { 37 | throw new Error(`You already have a plugin with slug ${input.slug}.`); 38 | } 39 | 40 | const templates = copyTemplates(createPluginCwd); 41 | const appliedTemplates = applyPromptsToTemplates(root.name, createPluginCwd, templates, input); 42 | applyPhpConstants(createPluginCwd, appliedTemplates, input.constantPrefix); 43 | applyPhpFunctions(createPluginCwd, input.constantPrefix); 44 | applySlug(root.name, createPluginCwd, input.slug); 45 | applyGitLabCi(createPluginCwd, input.constantPrefix, "wprjss"); 46 | applyPackageJson( 47 | root.name, 48 | createPluginCwd, 49 | { 50 | author: input.author, 51 | description: input.pluginDesc, 52 | homepage: input.pluginUri, 53 | version: input.pluginVersion 54 | }, 55 | fromWorkspace ? "1.0.0" : true 56 | ); 57 | modifyRootGitLabCiInclude("add", input.cwd, input.slug, "plugins"); 58 | generateLicenseFile(createPluginCwd, input.author, input.pluginDesc); 59 | logSuccess(`Successfully created plugin ${input.pluginName} in ${chalk.underline(createPluginCwd)}`); 60 | 61 | if (!fromWorkspace) { 62 | applyPhpNamespace(createPluginCwd, input.namespace, "plugin"); 63 | 64 | logProgress("Bootstrap and link new plugin..."); 65 | execa.sync("yarn", ["bootstrap"], { cwd: input.cwd, stdio: "inherit" }); 66 | execa.sync("yarn", ["lerna", "link"], { cwd: input.cwd, stdio: "inherit" }); 67 | 68 | runThirdPartyLicenseForPackage(createPluginCwd); 69 | regenerateLaunchJson(input.cwd); 70 | 71 | // First builds 72 | preInstallationBuilds(createPluginCwd); 73 | 74 | logProgress(`Rebuild the development environment, afterwards you can activate your new plugin...`); 75 | execa("yarn", ["docker:start"], { cwd: input.cwd, stdio: "inherit" }); 76 | } 77 | 78 | return createPluginCwd; 79 | } 80 | 81 | export { createPluginExecute }; 82 | -------------------------------------------------------------------------------- /src/create-plugin/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./applyPhpConstants"; 2 | export * from "./applyPhpFunctions"; 3 | export * from "./applyPromptsToTemplates"; 4 | export * from "./applySlug"; 5 | export * from "./copyTemplates"; 6 | export * from "./installPlugin"; 7 | export * from "./execute"; 8 | export * from "./program"; 9 | export * from "./prompt"; 10 | -------------------------------------------------------------------------------- /src/create-plugin/installPlugin.ts: -------------------------------------------------------------------------------- 1 | import { logProgress, logSuccess } from "../utils"; 2 | import execa from "execa"; 3 | 4 | /** 5 | * Do some basic builds for a plugin or package. 6 | * 7 | * @param answers 8 | */ 9 | function preInstallationBuilds(createPluginCwd: string) { 10 | logProgress("Start initial build process..."); 11 | execa.sync("yarn", ["build"], { cwd: createPluginCwd, stdio: "inherit" }); 12 | logSuccess("Successfully created first build"); 13 | 14 | logProgress("Run tests..."); 15 | execa.sync("yarn", ["test"], { cwd: createPluginCwd, stdio: "inherit" }); 16 | logSuccess("Successfully run tests"); 17 | 18 | logProgress("Start i18n generation..."); 19 | execa.sync("yarn", ["i18n:generate:backend"], { cwd: createPluginCwd, stdio: "inherit" }); 20 | execa.sync("yarn", ["i18n:generate:frontend"], { cwd: createPluginCwd, stdio: "inherit" }); 21 | logSuccess("Successfully generate .pot files in your plugin"); 22 | } 23 | 24 | export { preInstallationBuilds }; 25 | -------------------------------------------------------------------------------- /src/create-plugin/program.ts: -------------------------------------------------------------------------------- 1 | import { command } from "commander"; 2 | import { createPluginPrompt } from "./"; 3 | 4 | type CreatePluginOpts = { 5 | cwd: string; 6 | pluginName: string; 7 | slug: string; 8 | pluginUri: string; 9 | pluginDesc: string; 10 | author: string; 11 | authorUri: string; 12 | pluginVersion: string; 13 | minPhp: string; 14 | minWp: string; 15 | namespace: string; 16 | optPrefix: string; 17 | dbPrefix: string; 18 | constantPrefix: string; 19 | }; 20 | 21 | const createPluginCommand = command("create-plugin") 22 | .description("Create a new plugin within your workspace which you created previously.") 23 | .option("--cwd ", "The path to the workspace where the plugin should be created", process.cwd()) 24 | .option("--plugin-name ", "How should your plugin be named?") 25 | .option("--slug ", "What's the plugin slug (similar to folder name inside wp-content/plugins)?") 26 | .option("--plugin-uri ", "What's your plugins' homepage URL?") 27 | .option("--plugin-desc ", "Give a one-line plugin description?") 28 | .option("--author ", "Who is the plugin author (e. g. your wordpress.org username)?") 29 | .option("--author-uri ", "What's your author homepage URL?") 30 | .option("--plugin-version ", "What should be the initial version of the plugin?") 31 | .option( 32 | "--min-php ", 33 | "What's the minimum required PHP version (minimum of 7.0 required for the boilerplate)?" 34 | ) 35 | .option( 36 | "--min-wp ", 37 | "What's the minimum required WordPress version (minimum of 5.2 required for the boilerplate)?" 38 | ) 39 | .option("--namespace ", "What's the PHP Namespace (e. g. 'MatthiasWeb\\WPRJSS')?") 40 | .option("--opt-prefix ", "What's the WordPress options (wp_options) names prefix?") 41 | .option("--db-prefix ", "What's the WordPress database tables prefix?") 42 | .option("--constant-prefix ", "What's the PHP constants prefix?") 43 | .action((args) => createPluginPrompt(args)); 44 | 45 | export { CreatePluginOpts, createPluginCommand }; 46 | -------------------------------------------------------------------------------- /src/create-plugin/prompt.ts: -------------------------------------------------------------------------------- 1 | import { prompt } from "inquirer"; 2 | import { CreatePluginOpts, createPluginCommand, createPluginExecute } from "./"; 3 | import { 4 | getCommandDescriptionForPrompt, 5 | logError, 6 | inquirerRequiredValidate, 7 | caseAll, 8 | slugCamelCase, 9 | getGitConfig, 10 | isValidSemver, 11 | isValidUrl, 12 | isValidPhpNamespace, 13 | getInternalExampleArgs, 14 | checkValidWorkspace 15 | } from "../utils"; 16 | import { resolve } from "path"; 17 | import slugify from "slugify"; 18 | import { newsletterPrompt } from "../misc"; 19 | 20 | /** 21 | * Prompt for CLI arguments which are not passed. 22 | * 23 | * @param opts 24 | * @param before If you pass a callback the plugin itself will not be installed and build because it comes from create-workspace 25 | * @param after 26 | */ 27 | async function createPluginPrompt( 28 | { 29 | cwd, 30 | pluginName, 31 | slug, 32 | pluginUri, 33 | pluginDesc, 34 | author, 35 | authorUri, 36 | pluginVersion, 37 | minPhp, 38 | minWp, 39 | namespace, 40 | optPrefix, 41 | dbPrefix, 42 | constantPrefix 43 | }: Partial, 44 | before?: () => Promise, 45 | after?: (createPluginCwd: string, pluginData: CreatePluginOpts) => Promise 46 | ) { 47 | let root: any; 48 | const createWorkspaceCwd = cwd ? resolve(cwd) : process.cwd(); 49 | 50 | // It is coming directly from create-plugin command 51 | if (!before) { 52 | root = checkValidWorkspace(createWorkspaceCwd, createPluginCommand); 53 | } 54 | 55 | const mockData = getInternalExampleArgs("plugin"); 56 | const gitName = getGitConfig("user.name"); 57 | 58 | const answers = 59 | (mockData as any) || 60 | (await prompt( 61 | [ 62 | !pluginName && { 63 | name: "pluginName", 64 | message: getCommandDescriptionForPrompt(createPluginCommand, "--plugin-name"), 65 | type: "input", 66 | validate: inquirerRequiredValidate 67 | }, 68 | !slug && { 69 | name: "slug", 70 | message: getCommandDescriptionForPrompt(createPluginCommand, "--slug"), 71 | type: "input", 72 | default: (dAnswers: any) => { 73 | const useThis = (dAnswers.pluginName || pluginName) as string; 74 | return useThis 75 | ? slugify(useThis, { 76 | lower: true 77 | }).replace(/^wp-/, "") 78 | : undefined; 79 | }, 80 | validate: (value: string) => 81 | /^[A-Za-z0-9-_]+$/.test(value) ? true : "Your plugin slug should only contain [A-Za-z0-9-_]." 82 | }, 83 | !pluginUri && { 84 | name: "pluginUri", 85 | message: getCommandDescriptionForPrompt(createPluginCommand, "--plugin-uri"), 86 | type: "input", 87 | validate: (value: string) => (value ? isValidUrl(value, true) : true) 88 | }, 89 | !pluginDesc && { 90 | name: "pluginDesc", 91 | message: getCommandDescriptionForPrompt(createPluginCommand, "--plugin-desc"), 92 | type: "input" 93 | }, 94 | !author && { 95 | name: "author", 96 | message: getCommandDescriptionForPrompt(createPluginCommand, "--author"), 97 | type: "input", 98 | validate: inquirerRequiredValidate, 99 | default: gitName 100 | }, 101 | !authorUri && { 102 | name: "authorUri", 103 | message: getCommandDescriptionForPrompt(createPluginCommand, "--author-uri"), 104 | type: "input", 105 | validate: (value: string) => (value ? isValidUrl(value, true) : true) 106 | }, 107 | !pluginVersion && { 108 | name: "pluginVersion", 109 | message: getCommandDescriptionForPrompt(createPluginCommand, "--plugin-version"), 110 | type: "input", 111 | default: "1.0.0", 112 | validate: (value: string) => isValidSemver(value, true) 113 | }, 114 | !minPhp && { 115 | name: "minPhp", 116 | message: getCommandDescriptionForPrompt(createPluginCommand, "--min-php"), 117 | type: "input", 118 | default: "7.0.0", 119 | validate: (value: string) => isValidSemver(value, true) 120 | }, 121 | !minWp && { 122 | name: "minWp", 123 | message: getCommandDescriptionForPrompt(createPluginCommand, "--min-wp"), 124 | type: "input", 125 | default: "5.2.0", 126 | validate: (value: string) => isValidSemver(value, true) 127 | }, 128 | !namespace && { 129 | name: "namespace", 130 | message: getCommandDescriptionForPrompt(createPluginCommand, "--namespace"), 131 | type: "input", 132 | validate: (value: string) => { 133 | const required = inquirerRequiredValidate(value); 134 | if (required !== true) { 135 | return required; 136 | } 137 | return isValidPhpNamespace(value) ? true : "This is not a valid PHP namespace."; 138 | }, 139 | default: (dAnswers: any) => { 140 | const useThis = (dAnswers.author || author) as string; 141 | const useSlug = (dAnswers.slug || slug) as string; 142 | return useThis && useSlug 143 | ? slugCamelCase( 144 | slugify(useThis, { 145 | lower: true 146 | }), 147 | true 148 | ) + 149 | "\\" + 150 | slugCamelCase(useSlug, true) 151 | : undefined; 152 | } 153 | }, 154 | !optPrefix && { 155 | name: "optPrefix", 156 | message: getCommandDescriptionForPrompt(createPluginCommand, "--opt-prefix"), 157 | type: "input", 158 | validate: (value: string) => { 159 | const required = inquirerRequiredValidate(value); 160 | if (required !== true) { 161 | return required; 162 | } 163 | return /^[A-Za-z_]+$/.test(value) ? true : "This is not a valid option prefix."; 164 | }, 165 | default: (dAnswers: any) => { 166 | const result = ((dAnswers.slug || slug) as string).replace(/-/g, "_"); 167 | const availableFirstLetters = result.match(/_(.)/g); 168 | if (availableFirstLetters && availableFirstLetters.length > 1) { 169 | return result.charAt(0) + availableFirstLetters.map((o) => o.slice(1)).join(""); 170 | } 171 | return result; 172 | } 173 | }, 174 | !dbPrefix && { 175 | name: "dbPrefix", 176 | message: getCommandDescriptionForPrompt(createPluginCommand, "--db-prefix"), 177 | type: "input", 178 | validate: (value: string) => { 179 | const required = inquirerRequiredValidate(value); 180 | if (required !== true) { 181 | return required; 182 | } 183 | return /^[A-Za-z_]+$/.test(value) ? true : "This is not a valid database table prefix."; 184 | }, 185 | default: (dAnswers: any) => { 186 | const useThis = (dAnswers.optPrefix || optPrefix) as string; 187 | return useThis ? useThis.replace(/-/g, "_").replace(/^wp_/, "") : undefined; 188 | } 189 | }, 190 | !constantPrefix && { 191 | name: "constantPrefix", 192 | message: getCommandDescriptionForPrompt(createPluginCommand, "--constant-prefix"), 193 | type: "input", 194 | validate: (value: string) => { 195 | const required = inquirerRequiredValidate(value); 196 | if (required !== true) { 197 | return required; 198 | } 199 | return /^[A-Za-z_]+$/.test(value) ? true : "This is not a valid constant prefix."; 200 | }, 201 | default: (dAnswers: any) => { 202 | const useThis = (dAnswers.optPrefix || optPrefix) as string; 203 | return useThis ? useThis.toUpperCase().replace(/-/g, "_") : undefined; 204 | } 205 | } 206 | ].filter(Boolean) 207 | )); 208 | 209 | // If there is no plugin name given via CLI also ask for email marketing 210 | await newsletterPrompt(!(mockData as any) && !pluginName); 211 | 212 | try { 213 | let parsed = { 214 | cwd, 215 | pluginName, 216 | slug, 217 | pluginUri, 218 | pluginDesc, 219 | author, 220 | authorUri, 221 | pluginVersion, 222 | minPhp, 223 | minWp, 224 | namespace, 225 | optPrefix, 226 | dbPrefix, 227 | constantPrefix, 228 | ...answers 229 | }; 230 | parsed.namespace = parsed.namespace.replace(/\\\\/g, "\\"); 231 | parsed = caseAll(parsed, ["constantPrefix"], ["slug", "pluginUri", "authorUri", "optPrefix", "dbPrefix"]); 232 | if (before) { 233 | await before(); 234 | } 235 | 236 | // Root can be lazy due create-workspace command 237 | if (!root) { 238 | root = checkValidWorkspace(createWorkspaceCwd, createPluginCommand); 239 | } 240 | 241 | const createPluginCwd = await createPluginExecute(root, parsed, !!before); 242 | if (after) { 243 | await after(createPluginCwd, parsed); 244 | } 245 | } catch (e) { 246 | logError(e.toString()); 247 | } 248 | } 249 | 250 | export { createPluginPrompt }; 251 | -------------------------------------------------------------------------------- /src/create-workspace/applyPorts.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import { CreateWorkspaceOpts } from "./"; 3 | import { logProgress, searchAndReplace } from "../utils"; 4 | import glob from "glob"; 5 | 6 | /** 7 | * Apply ports to the local docker-compose environment. 8 | * 9 | * @param portWp 10 | * @param portPma 11 | * @param createCwd 12 | */ 13 | function applyPorts(portWp: CreateWorkspaceOpts["portWp"], portPma: CreateWorkspaceOpts["portPma"], createCwd: string) { 14 | logProgress( 15 | `Apply ports ${chalk.underline(portWp)} (WP) and ${chalk.underline(portPma)} (PMA) for local development...` 16 | ); 17 | const composeFiles = glob.sync("devops/docker-compose/docker-compose.local.yml", { 18 | cwd: createCwd, 19 | absolute: true 20 | }); 21 | const shFiles = glob.sync("devops/scripts/*.sh", { cwd: createCwd, absolute: true }); 22 | searchAndReplace(composeFiles, /"8080:80"/g, `"${portWp}:80"`); 23 | searchAndReplace(composeFiles, /"8079:80"/g, `"${portPma}:80"`); 24 | searchAndReplace(shFiles, /localhost:8080/g, `localhost:${portWp}`); 25 | } 26 | 27 | export { applyPorts }; 28 | -------------------------------------------------------------------------------- /src/create-workspace/applyWorkspaceName.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import { CreateWorkspaceOpts } from "./"; 3 | import glob from "glob"; 4 | import { logProgress, logSuccess, searchAndReplace } from "../utils"; 5 | 6 | /** 7 | * Apply workspace name to the given folder. 8 | * 9 | * @param workspace 10 | * @param createCwd 11 | */ 12 | function applyWorkspaceName(workspace: CreateWorkspaceOpts["workspace"], createCwd: string) { 13 | logProgress(`Apply workspace name ${chalk.underline(workspace)}...`); 14 | const globFiles = (pattern: string) => glob.sync(pattern, { cwd: createCwd, absolute: true }); 15 | const workspaceFiles = [ 16 | ...globFiles(".gitlab-ci.yml"), 17 | ...globFiles("package.json"), 18 | ...globFiles("{plugins,packages}/*/package.json") 19 | ]; 20 | searchAndReplace(workspaceFiles, /wp-reactjs-multi-starter/g, workspace); 21 | logSuccess(`Workspace is now branded as ${chalk.underline(workspace)}`); 22 | } 23 | 24 | export { applyWorkspaceName }; 25 | -------------------------------------------------------------------------------- /src/create-workspace/checkDependencies.ts: -------------------------------------------------------------------------------- 1 | import { logProgress, logSuccess, logError } from "../utils"; 2 | import execa, { ExecaSyncReturnValue } from "execa"; 3 | import chalk from "chalk"; 4 | import terminalLink from "terminal-link"; 5 | import { prompt } from "inquirer"; 6 | import { execSync } from "child_process"; 7 | 8 | const LINK_YARN = "https://yarnpkg.com/docs/install/"; 9 | const LINK_COMPOSER = "https://getcomposer.org/"; 10 | const LINK_DOCKER = "https://docs.docker.com/install/"; 11 | const LINK_DOCKER_COMPOSE = "https://docs.docker.com/compose/install/"; 12 | const LINK_WP_CLI = "https://wp-cli.org/#installing"; 13 | const LINK_PRESTISSIMO = "https://packagist.org/packages/hirak/prestissimo"; 14 | 15 | /** 16 | * Install a dependency manually. 17 | * 18 | * @param command 19 | */ 20 | async function installNow(command: string[]) { 21 | console.log("The following commands will run in order to install:"); 22 | command.forEach((c) => console.log(` - ${c}`)); 23 | const { install } = await prompt([ 24 | { 25 | name: "install", 26 | type: "confirm", 27 | message: "Would you like to install it now?", 28 | default: "n" 29 | } 30 | ]); 31 | if (install) { 32 | try { 33 | command.forEach((c) => execSync(c, { stdio: "inherit" })); 34 | return true; 35 | } catch (e) { 36 | logError("Could not be installed, please install manually!"); 37 | } 38 | } 39 | return false; 40 | } 41 | 42 | /** 43 | * Check needed dependencies and exit if something missing. 44 | */ 45 | async function checkDependencies() { 46 | let exit = false; 47 | let exec: ExecaSyncReturnValue; 48 | logProgress("Checking prerequesits..."); 49 | 50 | // Yarn 51 | try { 52 | exec = execa.sync("yarn", ["--version"]); 53 | logSuccess("├── Yarn v" + exec.stdout); 54 | } catch (e) { 55 | logError( 56 | `├── Missing ${chalk.underline( 57 | "yarn" 58 | )} (npm alternative which is used in WP ReactJS Starter), install it now: ${terminalLink( 59 | LINK_YARN, 60 | LINK_YARN 61 | )}` 62 | ); 63 | 64 | if (!(await installNow(["curl -o- -L https://yarnpkg.com/install.sh | bash"]))) { 65 | exit = true; 66 | } 67 | } 68 | 69 | // Composer 70 | try { 71 | exec = execa.sync("composer", ["--version"]); 72 | logSuccess("├── " + exec.stdout); 73 | } catch (e) { 74 | logError( 75 | `├── Missing ${chalk.underline("composer")} (PHP dependency manager), install it now: ${terminalLink( 76 | LINK_COMPOSER, 77 | LINK_COMPOSER 78 | )}` 79 | ); 80 | 81 | if ( 82 | !(await installNow([ 83 | `php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"`, 84 | `php -r "if (hash_file('sha384', 'composer-setup.php') === 'c5b9b6d368201a9db6f74e2611495f369991b72d9c8cbd3ffbc63edff210eb73d46ffbfce88669ad33695ef77dc76976') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"`, 85 | `php composer-setup.php`, 86 | `php -r "unlink('composer-setup.php');"`, 87 | `sudo mv composer.phar /usr/local/bin/composer` 88 | ])) 89 | ) { 90 | exit = true; 91 | } 92 | } 93 | 94 | // Docker 95 | try { 96 | exec = execa.sync("docker", ["--version"]); 97 | logSuccess("├── " + exec.stdout); 98 | } catch (e) { 99 | exit = true; 100 | logError( 101 | `├── Missing ${chalk.underline("docker")} (containerizing applications), install it now: ${terminalLink( 102 | LINK_DOCKER, 103 | LINK_DOCKER 104 | )}` 105 | ); 106 | 107 | if (!(await installNow(["curl -o- -L https://get.docker.com | ssh"]))) { 108 | exit = true; 109 | } 110 | } 111 | 112 | // Docker compose 113 | try { 114 | exec = execa.sync("docker-compose", ["--version"]); 115 | logSuccess("├── " + exec.stdout); 116 | } catch (e) { 117 | logError( 118 | `├── Missing ${chalk.underline( 119 | "docker-compose" 120 | )} (connect multiple containerized applications within a network), install it now: ${terminalLink( 121 | LINK_DOCKER_COMPOSE, 122 | LINK_DOCKER_COMPOSE 123 | )}` 124 | ); 125 | 126 | if (!(await installNow(["sudo pip install docker-compose"]))) { 127 | exit = true; 128 | } 129 | } 130 | 131 | // WP CLI 132 | try { 133 | exec = execa.sync("wp", ["--version"]); 134 | logSuccess("├── " + exec.stdout); 135 | } catch (e) { 136 | logError( 137 | `├── Missing ${chalk.underline("wp-cli")} (WordPress CLI), install it now: ${terminalLink( 138 | LINK_WP_CLI, 139 | LINK_WP_CLI 140 | )}` 141 | ); 142 | 143 | if ( 144 | !(await installNow([ 145 | `curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar`, 146 | `php wp-cli.phar --info`, 147 | `chmod +x wp-cli.phar`, 148 | `sudo mv wp-cli.phar /usr/local/bin/wp`, 149 | `wp --info` 150 | ])) 151 | ) { 152 | exit = true; 153 | } 154 | } 155 | 156 | // Prestissimo 157 | try { 158 | exec = execa.sync("composer", ["global", "show", "hirak/prestissimo"]); 159 | logSuccess("├── prestissimo"); 160 | } catch (e) { 161 | logError( 162 | `├── Missing optional ${chalk.underline("Prestissimo")} (Composer package), install it now: ${terminalLink( 163 | LINK_PRESTISSIMO, 164 | LINK_PRESTISSIMO 165 | )}` 166 | ); 167 | 168 | await installNow([`composer global require hirak/prestissimo`]); 169 | } 170 | 171 | if (exit) { 172 | process.exit(1); 173 | } 174 | } 175 | 176 | export { checkDependencies }; 177 | -------------------------------------------------------------------------------- /src/create-workspace/completeWorkspace.ts: -------------------------------------------------------------------------------- 1 | import execa from "execa"; 2 | import chalk from "chalk"; 3 | import { ProjectResult, runThirdPartyLicenses } from "./"; 4 | import { logProgress, logSuccess } from "../utils"; 5 | import { preInstallationBuilds } from "../create-plugin"; 6 | import terminalLink from "terminal-link"; 7 | import { regenerateLaunchJson } from "../misc"; 8 | 9 | /** 10 | * The workspace and initial plugin is created, do some installation process! 11 | * 12 | * @param createPluginCwd 13 | * @param createWorkspaceCwd 14 | * @param workspaceData 15 | * @param gitlabProject 16 | */ 17 | async function completeWorkspace( 18 | createPluginCwd: string, 19 | createWorkspaceCwd: string, 20 | createUtilsCwd: string, 21 | gitlabProject?: ProjectResult 22 | ) { 23 | // Install dependencies 24 | logProgress("Bootstrap monorepo and download dependencies..."); 25 | execa.sync("yarn", ["bootstrap"], { cwd: createWorkspaceCwd, stdio: "inherit" }); 26 | 27 | runThirdPartyLicenses(createWorkspaceCwd); 28 | regenerateLaunchJson(createWorkspaceCwd); 29 | 30 | // Prompt first build processes 31 | logSuccess( 32 | "\n\nThe workspace is now usable. For first usage it is recommend to do some first tests and builds to check all is working fine." 33 | ); 34 | 35 | preInstallationBuilds(createUtilsCwd); 36 | preInstallationBuilds(createPluginCwd); 37 | 38 | // Start VSCode if available 39 | try { 40 | execa("code", [createWorkspaceCwd], { 41 | detached: true 42 | }); 43 | } catch (e) { 44 | // Silence is golden. 45 | } 46 | 47 | // Push changes 48 | if (gitlabProject) { 49 | logProgress("Push complete code to repository..."); 50 | execa.sync("git", ["add", "-A"], { cwd: createWorkspaceCwd, stdio: "inherit" }); 51 | execa.sync("git", ["commit", "-m 'chore: initial commit'", "--no-verify"], { 52 | cwd: createWorkspaceCwd, 53 | stdio: "inherit" 54 | }); 55 | execa.sync("git", ["push", "origin", "develop"], { cwd: createWorkspaceCwd, stdio: "inherit" }); 56 | logSuccess( 57 | `Successfully pushed code, see your CI/CD pipeline running ${terminalLink( 58 | "here", 59 | gitlabProject.web_url + "/pipelines" 60 | )}` 61 | ); 62 | } 63 | 64 | logProgress(`Initially start the development environment with ${chalk.underline("yarn docker:start")}...`); 65 | execa("yarn", ["docker:start"], { cwd: createWorkspaceCwd, stdio: "inherit" }); 66 | } 67 | 68 | export { completeWorkspace }; 69 | -------------------------------------------------------------------------------- /src/create-workspace/createDotEnv.ts: -------------------------------------------------------------------------------- 1 | import { writeFileSync } from "fs"; 2 | import { resolve } from "path"; 3 | import { CreateWorkspaceOpts } from "."; 4 | import { DEFAULT_ENCODING } from "../utils"; 5 | 6 | /** 7 | * Create a predefined .env file. 8 | * 9 | * @param createCwd 10 | * @param input 11 | */ 12 | function createDotEnv(createCwd: string, input: CreateWorkspaceOpts) { 13 | const dotEnvFile = resolve(createCwd, ".env"); 14 | const pairs = {} as { [key: string]: string }; 15 | 16 | if (input.remote) { 17 | pairs["WP_LOCAL_INSTALL_DIR"] = input.remote; 18 | } 19 | 20 | if (Object.keys(pairs).length > 0) { 21 | let content = ""; 22 | Object.keys(pairs).forEach((key) => (content += `${key}=${pairs[key]}\n`)); 23 | 24 | writeFileSync(dotEnvFile, content, { encoding: DEFAULT_ENCODING }); 25 | } 26 | } 27 | 28 | export { createDotEnv }; 29 | -------------------------------------------------------------------------------- /src/create-workspace/createGitFolder.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import execa from "execa"; 3 | import { resolve, basename } from "path"; 4 | import rimraf from "rimraf"; 5 | import { CreateWorkspaceOpts, ProjectResult } from "./"; 6 | import { logProgress, logSuccess } from "../utils"; 7 | import { moveSync, unlinkSync } from "fs-extra"; 8 | 9 | /** 10 | * Clone the repository and disconnect it. 11 | * 12 | * @param checkout 13 | * @param repository 14 | * @param createCwd 15 | * @param gitlabProject 16 | */ 17 | function createGitFolder( 18 | checkout: CreateWorkspaceOpts["checkout"], 19 | repository: CreateWorkspaceOpts["repository"], 20 | createCwd: string, 21 | gitlabProject?: ProjectResult 22 | ) { 23 | // Start cloning the repository 24 | logProgress(`Download repository ${chalk.underline(repository)} ref ${chalk.underline(checkout)}...`); 25 | execa.sync("git", ["clone", repository, basename(createCwd)], { stdio: "inherit" }); 26 | execa.sync("git", ["checkout", checkout], { cwd: createCwd, stdio: "inherit" }); 27 | rimraf.sync(resolve(createCwd, ".git")); 28 | 29 | // If there is a GitLab project, resolve the SSH checkout and move the .git configuration to the original folder 30 | if (gitlabProject) { 31 | const downloadCloneTarget = createCwd + "-temp"; 32 | logProgress(`Initialize into new repository ${chalk.underline(gitlabProject.ssh_url_to_repo)}...`); 33 | execa.sync("git", ["clone", gitlabProject.ssh_url_to_repo, basename(downloadCloneTarget)], { 34 | stdio: "inherit" 35 | }); 36 | execa.sync("git", ["checkout", "develop"], { cwd: downloadCloneTarget, stdio: "inherit" }); 37 | moveSync(downloadCloneTarget + "/.git", createCwd + "/.git"); 38 | rimraf.sync(downloadCloneTarget); 39 | } 40 | 41 | // Remove root LICENSE file 42 | unlinkSync(resolve(createCwd, "LICENSE")); 43 | 44 | logSuccess(`Workspace successfully created in ${chalk.underline(createCwd)}`); 45 | } 46 | 47 | export { createGitFolder }; 48 | -------------------------------------------------------------------------------- /src/create-workspace/execute.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from "path"; 2 | import { 3 | CreateWorkspaceOpts, 4 | createGitFolder, 5 | completeWorkspace, 6 | removeExamplePlugin, 7 | applyWorkspaceName, 8 | applyPorts, 9 | promptGitLab, 10 | ProjectResult 11 | } from "./"; 12 | import { createPluginPrompt } from "../create-plugin"; 13 | import { applyName } from "../create-package"; 14 | import { generateLicenseFile } from "../utils"; 15 | import { applyPackageJson, applyPhpNamespace } from "../misc"; 16 | import { createDotEnv } from "./createDotEnv"; 17 | 18 | /** 19 | * Generate a new workspace from a given repository and disconnect it. 20 | * Also do some garbage collection and movements for other commands. 21 | */ 22 | async function createWorkspaceExecute(input: CreateWorkspaceOpts) { 23 | const createCwd = resolve(process.cwd(), input.workspace); 24 | const utilsPath = resolve(createCwd, "packages/utils"); 25 | const gitlabProjectCreator = await promptGitLab(input.workspace); 26 | let gitLabProject: ProjectResult; 27 | 28 | // Run create-plugin command without installation (because this is done below) 29 | // So we have all prompts in one flow, awesome! 30 | await createPluginPrompt( 31 | { 32 | cwd: createCwd 33 | }, 34 | async () => { 35 | if (gitlabProjectCreator !== false) { 36 | gitLabProject = await gitlabProjectCreator(); 37 | } 38 | createGitFolder(input.checkout, input.repository, createCwd, gitLabProject); 39 | createDotEnv(createCwd, input); 40 | removeExamplePlugin(createCwd); 41 | applyWorkspaceName(input.workspace, createCwd); 42 | applyPorts(input.portWp, input.portPma, createCwd); 43 | }, 44 | async (createPluginCwd, pluginData) => { 45 | // Brand first package: utils 46 | const splitNs = pluginData.namespace.split("\\"); 47 | const utilsNamespace = 48 | (splitNs.length > 1 ? splitNs.slice(0, -1).join("\\") : pluginData.namespace) + "\\Utils"; 49 | 50 | applyPhpNamespace(utilsPath, utilsNamespace, "utils"); 51 | 52 | // Re-apply namespace of utils package for this plugin because before it did not know the namespace 53 | applyPhpNamespace(createPluginCwd, pluginData.namespace, "plugin"); 54 | 55 | applyName(utilsPath, "utils"); 56 | applyPackageJson( 57 | input.workspace, 58 | utilsPath, 59 | { 60 | author: pluginData.author, 61 | description: "Utility functionality for all your WordPress plugins.", 62 | homepage: pluginData.authorUri, 63 | version: "1.0.0" 64 | }, 65 | false 66 | ); 67 | generateLicenseFile(utilsPath, pluginData.author, pluginData.pluginDesc); 68 | 69 | // Complete 70 | await completeWorkspace(createPluginCwd, createCwd, utilsPath, gitLabProject); 71 | } 72 | ); 73 | } 74 | 75 | export { createWorkspaceExecute }; 76 | -------------------------------------------------------------------------------- /src/create-workspace/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./applyPorts"; 2 | export * from "./runThirdPartyLicenses"; 3 | export * from "./applyWorkspaceName"; 4 | export * from "./checkDependencies"; 5 | export * from "./completeWorkspace"; 6 | export * from "./createGitFolder"; 7 | export * from "./execute"; 8 | export * from "./program"; 9 | export * from "./prompt"; 10 | export * from "./promptGitLab"; 11 | export * from "./removeExamplePlugin"; 12 | export * from "./createDotEnv"; 13 | -------------------------------------------------------------------------------- /src/create-workspace/program.ts: -------------------------------------------------------------------------------- 1 | import { command } from "commander"; 2 | import { createWorkspacePrompt } from "./"; 3 | 4 | type CreateWorkspaceOpts = { 5 | workspace: string; 6 | repository: string; 7 | checkout: string; 8 | remote: string; 9 | portWp: number; 10 | portPma: number; 11 | }; 12 | 13 | const createWorkspaceCommand = command("create-workspace") 14 | .description("Creates a new workspace where your plugins are placed.") 15 | .option( 16 | "-w, --workspace [value]", 17 | "What's the name of your workspace (similar to your future git repository name)?", 18 | "" 19 | ) 20 | .option("-r, --repository [value]", "The repository URL", "https://github.com/devowlio/wp-react-starter") 21 | .option("-g, --checkout [value]", "The checkout ref", "master") 22 | .option("--remote ", "Which URL should be used for WordPress development? Leave empty for `localhost`") 23 | .option("--port-wp ", "Which port should be used for the local WordPress development?", (i) => +i) 24 | .option("--port-pma ", "Which port should be used for the local phpMyAdmin development?", (i) => +i) 25 | .action((args) => createWorkspacePrompt(args)); 26 | 27 | export { CreateWorkspaceOpts, createWorkspaceCommand }; 28 | -------------------------------------------------------------------------------- /src/create-workspace/prompt.ts: -------------------------------------------------------------------------------- 1 | import { prompt } from "inquirer"; 2 | import { CreateWorkspaceOpts, createWorkspaceCommand, createWorkspaceExecute, checkDependencies } from "./"; 3 | import { 4 | getCommandDescriptionForPrompt, 5 | logError, 6 | caseAll, 7 | inquirerRequiredValidate, 8 | getInternalExampleArgs, 9 | isValidUrl 10 | } from "../utils"; 11 | 12 | /** 13 | * Prompt for CLI arguments which are not passed. 14 | */ 15 | async function createWorkspacePrompt({ 16 | workspace, 17 | repository, 18 | checkout, 19 | remote, 20 | portWp, 21 | portPma 22 | }: CreateWorkspaceOpts) { 23 | await checkDependencies(); 24 | 25 | const mockData = getInternalExampleArgs("workspace"); 26 | 27 | const answers = 28 | (mockData as any) || 29 | (await prompt( 30 | [ 31 | !workspace && { 32 | name: "workspace", 33 | message: getCommandDescriptionForPrompt(createWorkspaceCommand, "--workspace"), 34 | type: "input", 35 | validate: (value: string) => { 36 | if (value && /^[A-Za-z0-9-_]+$/.test(value)) { 37 | return true; 38 | } 39 | return "Your workspace name should only contain [A-Za-z0-9-_]"; 40 | } 41 | }, 42 | !remote && { 43 | name: "remote", 44 | message: getCommandDescriptionForPrompt(createWorkspaceCommand, "--remote"), 45 | type: "input", 46 | validate: (value: string) => { 47 | return !value ? true : isValidUrl(value, true); 48 | } 49 | }, 50 | !portWp && { 51 | name: "portWp", 52 | message: getCommandDescriptionForPrompt(createWorkspaceCommand, "--port-wp"), 53 | type: "number", 54 | default: (answers: any) => { 55 | const useRemote = (answers.remote as string) || remote; 56 | if (useRemote) { 57 | return new URL(useRemote).port || 80; 58 | } 59 | return 8080; 60 | }, 61 | validate: inquirerRequiredValidate 62 | }, 63 | !portPma && { 64 | name: "portPma", 65 | message: getCommandDescriptionForPrompt(createWorkspaceCommand, "--port-pma"), 66 | type: "number", 67 | default: (answers: any) => { 68 | const useThis = (answers.portWp || +portWp) as number; 69 | return useThis > 0 ? useThis + 1 : 8079; 70 | }, 71 | validate: (value: number, answers: any) => { 72 | if (value === (answers.portWp || +portWp)) { 73 | return "You can not use the port twice."; 74 | } 75 | return true; 76 | } 77 | } 78 | ].filter(Boolean) 79 | )); 80 | 81 | try { 82 | const parsed = { workspace, repository, checkout, remote, portWp, portPma, ...answers }; 83 | await createWorkspaceExecute(caseAll(parsed, [], ["workspace"])); 84 | } catch (e) { 85 | logError(e.toString()); 86 | } 87 | } 88 | 89 | export { createWorkspacePrompt }; 90 | -------------------------------------------------------------------------------- /src/create-workspace/promptGitLab.ts: -------------------------------------------------------------------------------- 1 | import execa from "execa"; 2 | import { Gitlab } from "gitlab"; 3 | import { prompt } from "inquirer"; 4 | import { logProgress, logSuccess, logError, isValidUrl, inquirerRequiredValidate } from "../utils"; 5 | import terminalLink from "terminal-link"; 6 | import chalk from "chalk"; 7 | import { CreateWorkspaceOpts } from "./program"; 8 | 9 | type ProjectResult = { 10 | [key: string]: any; 11 | id: number; 12 | name: string; 13 | name_with_namespace: string; 14 | path: string; 15 | path_with_namespace: string; 16 | ssh_url_to_repo: string; 17 | web_url: string; 18 | }; 19 | 20 | /** 21 | * Prompt for GitLab auth and create the repository in the given group / namespace. 22 | * It exits automatically if something went wrong (for example SSH check) 23 | * 24 | * @param workspace 25 | * @returns `false` or `any` representing the new project 26 | */ 27 | async function promptGitLab(workspace: CreateWorkspaceOpts["workspace"]) { 28 | if (process.env.SKIP_PROMPT) { 29 | return false; 30 | } 31 | 32 | const { doGitlab } = await prompt([ 33 | { 34 | name: "doGitlab", 35 | type: "confirm", 36 | message: "Would you like to connect with your GitLab and automatically create and push to a new repository?" 37 | } 38 | ]); 39 | 40 | if (doGitlab) { 41 | const { host, token } = await prompt([ 42 | { 43 | name: "host", 44 | type: "input", 45 | message: "What's your GitLab instance URL?", 46 | default: process.env.GITLAB_HOST || "https://gitlab.com", 47 | validate: (value: string) => isValidUrl(value, true) 48 | }, 49 | { 50 | name: "token", 51 | type: "input", 52 | message: `What's your ${terminalLink( 53 | "personal token", 54 | "https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html" 55 | )} (click the link to learn how to obtain a token; token must have ${chalk.underline( 56 | "api,write_repository" 57 | )} scope)?`, 58 | default: process.env.GITLAB_TOKEN, 59 | validate: inquirerRequiredValidate 60 | } 61 | ]); 62 | 63 | // Check if SSH was setup correctly 64 | logProgress("Check if SSH is setup correctly..."); 65 | try { 66 | execa.sync("ssh", ["-T", "git@" + host.replace(/^https?:\/\//, "")]); 67 | } catch (e) { 68 | logError( 69 | `SSH key is not setup for this host. Please checkout this ${terminalLink( 70 | "documentation", 71 | "https://docs.gitlab.com/ee/ssh/" 72 | )}. You can rerun this command without creating a GitLab repository automatically.` 73 | ); 74 | process.exit(1); 75 | } 76 | 77 | // The typed api, make it only available when successfully logged in through the personal token 78 | const api = new Gitlab({ 79 | host, 80 | token, 81 | rejectUnauthorized: true 82 | }); 83 | logProgress("Loading available possible targets for the new repository..."); 84 | const groups = (await api.Groups.all()) as any[]; 85 | const user = (await api.Users.current()) as any; 86 | let namespaceId: number; 87 | 88 | if (groups && groups.length) { 89 | const { group } = await prompt([ 90 | { 91 | name: "group", 92 | message: "Where in GitLab (Group) do you want to create the repository?", 93 | type: "list", 94 | choices: groups 95 | .map((g) => ({ 96 | name: g.full_name, 97 | value: g 98 | })) 99 | .concat([ 100 | { 101 | name: "Assign to myself, " + user.username, 102 | value: "" 103 | } 104 | ]) 105 | } 106 | ]); 107 | namespaceId = group ? group.id : undefined; 108 | } 109 | 110 | /** 111 | * Make the creation lazy by a closure function. 112 | */ 113 | const creator = async () => { 114 | // Create the repository in the given namespace 115 | logProgress("Create GitLab project..."); 116 | const project = (await api.Projects.create({ 117 | name: workspace, 118 | namespace_id: namespaceId // eslint-disable-line @typescript-eslint/camelcase 119 | })) as ProjectResult; 120 | logSuccess(`Successfully created project ${chalk.underline(project.name_with_namespace)}`); 121 | 122 | // Create the protected develop branch 123 | logProgress("Setup repository..."); 124 | await api.Branches.create(project.id, "develop", "master"); 125 | await api.Projects.edit(project.id, { 126 | default_branch: "develop" // eslint-disable-line @typescript-eslint/camelcase 127 | }); 128 | logSuccess(`Successfully created default branch ${chalk.underline("develop")}`); 129 | return project; 130 | }; 131 | return creator; 132 | } 133 | 134 | return false; 135 | } 136 | 137 | export { ProjectResult, promptGitLab }; 138 | -------------------------------------------------------------------------------- /src/create-workspace/removeExamplePlugin.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import { resolve } from "path"; 3 | import { logProgress, FOLDER_CWRA, DEFAULT_ENCODING } from "../utils"; 4 | import { renameSync, writeFileSync, mkdirSync } from "fs"; 5 | import { copySync } from "fs-extra"; 6 | import rimraf from "rimraf"; 7 | import { modifyRootGitLabCiInclude } from "../misc"; 8 | 9 | /** 10 | * Remove first initial plugin and move to create-wp-react-app for others commands. 11 | * Also alter the .gitlab-ci.yml includes and CHANGELOG.md. 12 | * 13 | * @param createCwd 14 | */ 15 | function removeExamplePlugin(createCwd: string) { 16 | logProgress( 17 | `Prepare monorepo for future commands like ${chalk.underline("create-plugin")} and ${chalk.underline( 18 | "create-package" 19 | )}...` 20 | ); 21 | writeFileSync(resolve(createCwd, "plugins/wp-reactjs-starter/CHANGELOG.md"), "", { encoding: DEFAULT_ENCODING }); 22 | writeFileSync(resolve(createCwd, "packages/utils/CHANGELOG.md"), "", { encoding: DEFAULT_ENCODING }); 23 | renameSync(resolve(createCwd, "plugins/wp-reactjs-starter"), resolve(createCwd, FOLDER_CWRA, "template")); 24 | 25 | // Copy utils package for "create-package" command 26 | const tmplPackageFolder = resolve(createCwd, FOLDER_CWRA, "template-package"); 27 | copySync(resolve(createCwd, "packages/utils"), tmplPackageFolder); 28 | 29 | rimraf.sync(resolve(tmplPackageFolder, "lib")); 30 | rimraf.sync(resolve(tmplPackageFolder, "src")); 31 | rimraf.sync(resolve(tmplPackageFolder, "test/jest")); 32 | rimraf.sync(resolve(tmplPackageFolder, "test/phpunit")); 33 | 34 | mkdirSync(resolve(tmplPackageFolder, "lib")); 35 | mkdirSync(resolve(tmplPackageFolder, "src")); 36 | mkdirSync(resolve(tmplPackageFolder, "test/jest")); 37 | mkdirSync(resolve(tmplPackageFolder, "test/phpunit")); 38 | 39 | modifyRootGitLabCiInclude("remove", createCwd, "wp-reactjs-starter", "plugins"); 40 | } 41 | 42 | export { removeExamplePlugin }; 43 | -------------------------------------------------------------------------------- /src/create-workspace/runThirdPartyLicenses.ts: -------------------------------------------------------------------------------- 1 | import execa from "execa"; 2 | import { logProgress, logSuccess } from "../utils"; 3 | 4 | /** 5 | * Generate all disclaimer files for the current workspace. 6 | * 7 | * @param createCwd 8 | */ 9 | function runThirdPartyLicenses(createCwd: string) { 10 | logProgress("Start generating 3rd party disclaimer..."); 11 | execa.sync("yarn", ["--silent", "workspace:concurrently"], { 12 | cwd: createCwd, 13 | env: { 14 | WORKSPACE_COMMAND: "yarn --silent grunt composer:disclaimer --force 2>/dev/null" 15 | } 16 | }); 17 | execa.sync("yarn", ["--silent", "workspace:concurrently"], { 18 | cwd: createCwd, 19 | env: { 20 | WORKSPACE_COMMAND: "yarn --silent grunt yarn:disclaimer --force 2>/dev/null" 21 | } 22 | }); 23 | logSuccess("Successfully generated "); 24 | } 25 | 26 | export { runThirdPartyLicenses }; 27 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { parse } from "commander"; 4 | import "./create-workspace"; 5 | import "./create-plugin"; 6 | import "./create-package"; 7 | 8 | const program = parse(process.argv); 9 | 10 | // If no argument is passed show help 11 | if (process.argv.length < 3) { 12 | program.help(); 13 | } 14 | -------------------------------------------------------------------------------- /src/misc/applyGitLabCi.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import glob from "glob"; 3 | import { logProgress, searchAndReplace } from "../utils"; 4 | 5 | /** 6 | * Find GitLab CI jobs and replace them with the correct prefix (depending on constant prefix). 7 | * 8 | * This does not add the include to the root .gitlab-ci.yml! 9 | * 10 | * @param createPackageCwd 11 | * @param constantPrefix 12 | * @param prefixToReplace 13 | */ 14 | function applyGitLabCi( 15 | createPackageCwd: string, 16 | constantPrefix: string, 17 | /** 18 | * Why "utils " and "utils"? 19 | * 20 | * The package uses same name and abbreviation for folder path and package names. 21 | * 22 | * "utils " reflects all job definitions and needs always be the first replacement. 23 | */ 24 | prefixToReplace: "wprjss" | "utils" | "utils " 25 | ) { 26 | const jobPrefix = constantPrefix.toLowerCase(); 27 | logProgress(`Find GitLab CI jobs and prefix them with ${chalk.underline(jobPrefix)}...`); 28 | const globFiles = (pattern: string) => glob.sync(pattern, { cwd: createPackageCwd, absolute: true }); 29 | const files = [...globFiles("devops/.gitlab/**/*.yml"), ...globFiles("devops/.gitlab/.gitlab-ci.yml")]; 30 | searchAndReplace(files, new RegExp(prefixToReplace, "g"), jobPrefix); 31 | } 32 | 33 | export { applyGitLabCi }; 34 | -------------------------------------------------------------------------------- /src/misc/applyPackageJson.ts: -------------------------------------------------------------------------------- 1 | import { logProgress, logSuccess, DEFAULT_ENCODING } from "../utils"; 2 | import chalk from "chalk"; 3 | import { resolve } from "path"; 4 | import { readFileSync, writeFileSync } from "fs"; 5 | 6 | /** 7 | * Update package.json with new utils version and author information. 8 | * Each package also contains a composer.json, update it too... 9 | * 10 | * It does not update the name of the package! 11 | * 12 | * @param root 13 | * @param createPackageCwd 14 | * @param input 15 | * @param updateUtilsVersion 16 | */ 17 | function applyPackageJson( 18 | root: string, 19 | createPackageCwd: string, 20 | input: { 21 | version: string; 22 | description: string; 23 | author: string; 24 | homepage: string; 25 | }, 26 | updateUtilsVersion: boolean | string = true 27 | ) { 28 | const packageJsonPath = resolve(createPackageCwd, "package.json"); 29 | const composerJsonPath = resolve(createPackageCwd, "composer.json"); 30 | 31 | logProgress(`Update ${chalk.underline(packageJsonPath)}...`); 32 | let packageJson = readFileSync(packageJsonPath, { encoding: DEFAULT_ENCODING }); 33 | 34 | packageJson = packageJson.replace(/"version":\s*"([0-9.]+)"/g, '"version": "' + input.version + '"'); 35 | packageJson = packageJson.replace(/"description":\s*"([^"]+)"/g, '"description": "' + input.description + '"'); 36 | packageJson = packageJson.replace(/"author":\s*"([^"]+)"/g, '"author": "' + input.author + '"'); 37 | packageJson = packageJson.replace(/"homepage":\s*"([^"]+)"/g, '"homepage": "' + input.homepage + '"'); 38 | 39 | // Update utils version 40 | if (updateUtilsVersion) { 41 | const utilsVersion = 42 | updateUtilsVersion === true 43 | ? require(resolve(createPackageCwd, "../../packages/utils/package.json")).version 44 | : updateUtilsVersion; 45 | packageJson = packageJson.replace( 46 | new RegExp('"@' + root + '\\/utils":\\s*"\\^([0-9.]+)"', "g"), 47 | '"@' + root + '/utils": "^' + utilsVersion + '"' 48 | ); 49 | } 50 | 51 | writeFileSync(packageJsonPath, packageJson, { encoding: DEFAULT_ENCODING }); 52 | logSuccess(`Successfully updated ${chalk.underline(packageJsonPath)}`); 53 | 54 | // Update composer.json 55 | logProgress(`Update ${chalk.underline(composerJsonPath)}...`); 56 | let composerJson = readFileSync(composerJsonPath, { encoding: DEFAULT_ENCODING }); 57 | 58 | composerJson = composerJson.replace(/"description":\s*"([^"]+)"/g, '"description": "' + input.description + '"'); 59 | composerJson = composerJson.replace(/Matthias Günter/g, input.author); 60 | 61 | writeFileSync(composerJsonPath, composerJson, { encoding: DEFAULT_ENCODING }); 62 | logSuccess(`Successfully updated ${chalk.underline(composerJsonPath)}`); 63 | } 64 | 65 | export { applyPackageJson }; 66 | -------------------------------------------------------------------------------- /src/misc/applyPhpNamespace.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from "path"; 2 | import { readFileSync } from "fs"; 3 | import { logProgress, searchAndReplace, logSuccess, DEFAULT_ENCODING } from "../utils"; 4 | import chalk from "chalk"; 5 | import glob from "glob"; 6 | 7 | function applyPhpNamespace(createPackageCwd: string, namespace: string, type: "utils" | "plugin") { 8 | // Apply the namespace 9 | logProgress(`Apply namespace ${chalk.underline(namespace)} to all PHP files for autoloading...`); 10 | const globFiles = (pattern: string) => glob.sync(pattern, { cwd: createPackageCwd, absolute: true }); 11 | 12 | // Define our new package 13 | searchAndReplace( 14 | globFiles("composer.json"), 15 | type === "utils" ? /MatthiasWeb\\\\Utils/g : /MatthiasWeb\\\\WPRJSS/g, 16 | namespace.replace(/\\/g, "\\\\") 17 | ); 18 | 19 | // Search for namespaces in source and test files 20 | const phpFiles = [...globFiles("src/**/*.php"), ...globFiles("test/phpunit/**/*.php")]; 21 | searchAndReplace(phpFiles, type === "utils" ? /MatthiasWeb\\Utils/g : /MatthiasWeb\\WPRJSS/g, namespace); 22 | 23 | // Define autoloading for utils package in source and test files 24 | if (type === "plugin") { 25 | const utilsPluginReceiverFile = readFileSync( 26 | resolve(createPackageCwd, "../../packages/utils/src/PluginReceiver.php"), 27 | { encoding: DEFAULT_ENCODING } 28 | ); 29 | const namespaceUtils = utilsPluginReceiverFile.match(/^namespace ([^;]+)/m)[1]; 30 | 31 | searchAndReplace(globFiles("composer.lock"), /MatthiasWeb\\\\Utils/g, namespaceUtils.replace(/\\/g, "\\\\")); 32 | searchAndReplace(phpFiles, /MatthiasWeb\\Utils/g, namespaceUtils); 33 | } 34 | 35 | logSuccess(`Successfully applied PHP namespace to package!`); 36 | } 37 | 38 | export { applyPhpNamespace }; 39 | -------------------------------------------------------------------------------- /src/misc/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./applyGitLabCi"; 2 | export * from "./modifyRootGitLabCiInclude"; 3 | export * from "./applyPackageJson"; 4 | export * from "./applyPhpNamespace"; 5 | export * from "./regenerateLaunchJson"; 6 | export * from "./newsletterPrompt"; 7 | -------------------------------------------------------------------------------- /src/misc/modifyRootGitLabCiInclude.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import { logProgress, DEFAULT_ENCODING, logSuccess } from "../utils"; 3 | import { resolve } from "path"; 4 | import { readFileSync, writeFileSync } from "fs"; 5 | 6 | /** 7 | * Modify the anchor content of the root .gitlab-ci.yml file to add or 8 | * remove a plugin's or package CI configuration. 9 | * 10 | * @param action 11 | * @param cwd 12 | * @param slug 13 | */ 14 | function modifyRootGitLabCiInclude(action: "add" | "remove", cwd: string, slug: string, type: "plugins" | "packages") { 15 | logProgress(`Modify root .gitlab-ci.yml and ${action} include ${chalk.underline(slug)}...`); 16 | const path = resolve(cwd, ".gitlab-ci.yml"); 17 | let content = readFileSync(path, { encoding: DEFAULT_ENCODING }); 18 | content = content.replace( 19 | /# create-wp-react-app -->\n(.*)\n(\s*)# <-- create-wp-react-app/gms, 20 | (match, inner: string, spaceBefore: string) => { 21 | let newLines = inner.split("\n").filter(Boolean); 22 | if (action === "remove") { 23 | newLines = newLines.filter((line) => line.indexOf("- /" + type + "/" + slug + "/") === -1); 24 | } else { 25 | newLines.push(spaceBefore + "- /" + type + "/" + slug + "/devops/.gitlab/.gitlab-ci.yml"); 26 | } 27 | return `# create-wp-react-app -->\n${newLines.join("\n")}\n${spaceBefore}# <-- create-wp-react-app`; 28 | } 29 | ); 30 | writeFileSync(path, content, { encoding: DEFAULT_ENCODING }); 31 | logSuccess(`Successfully updated ${path}`); 32 | } 33 | 34 | export { modifyRootGitLabCiInclude }; 35 | -------------------------------------------------------------------------------- /src/misc/newsletterPrompt.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import { prompt } from "inquirer"; 3 | import { logProgress, logSuccess, logError } from "../utils"; 4 | import terminalLink from "terminal-link"; 5 | import axios from "axios"; 6 | 7 | async function newsletterPrompt(condition: boolean) { 8 | const { email }: { email: string } = 9 | condition && 10 | (await prompt([ 11 | { 12 | name: "email", 13 | message: `You will ♥ create-wp-react-app and wp-react-starter! Would you like to be informed about updates via e-mail (you agree ${terminalLink( 14 | "devowl.io privacy policy", 15 | "https://devowl.io/privacy-policy/" 16 | )})? Enter your e-mail:`, 17 | type: "input", 18 | validate: (value: string) => 19 | // eslint-disable-next-line no-useless-escape 20 | !value || 21 | /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test( 22 | value 23 | ) 24 | ? true 25 | : "This is not an valid email." 26 | } 27 | ])); 28 | 29 | if (email) { 30 | logProgress("Registering on newsletter..."); 31 | try { 32 | await axios.post("https://devowl.io/wp-json/devowl-site/v1/plugin-activation-newsletter", { 33 | email, 34 | referer: "localhost", 35 | slug: "create-wp-react-app" 36 | }); 37 | logSuccess(`Successfully registered ${chalk.underline(email)}!`); 38 | } catch (e) { 39 | logError(`Error while newsletter registration, skipping (${e})...`); 40 | } 41 | } 42 | } 43 | 44 | export { newsletterPrompt }; 45 | -------------------------------------------------------------------------------- /src/misc/regenerateLaunchJson.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import execa from "execa"; 3 | import { logProgress } from "../utils"; 4 | 5 | /** 6 | * Regenerate launch.json file. 7 | * 8 | * @param createCwd 9 | */ 10 | function regenerateLaunchJson(createCwd: string) { 11 | logProgress(`Regenerate ${chalk.underline("launch.json")} for debug purposes...`); 12 | execa.sync("yarn", ["debug:php:generate"], { cwd: createCwd, stdio: "inherit" }); 13 | } 14 | 15 | export { regenerateLaunchJson }; 16 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import execa from "execa"; 2 | import chalk from "chalk"; 3 | import { command, Option, Command } from "commander"; 4 | import { resolve, join } from "path"; 5 | import { readFileSync, writeFileSync } from "fs"; 6 | import { URL } from "url"; 7 | import { CreateWorkspaceOpts } from "./create-workspace"; 8 | import { CreatePluginOpts } from "./create-plugin"; 9 | import { existsSync } from "fs-extra"; 10 | import { CreatePackageOpts } from "./create-package"; 11 | 12 | const FOLDER_CWRA = "common/create-wp-react-app"; 13 | 14 | const DEFAULT_ENCODING = "UTF-8"; 15 | 16 | const logProgress = (text: string) => console.log(chalk.black.bgCyan(text)); 17 | const logSuccess = (text: string) => console.log(chalk.black.green(text)); 18 | const logError = (text: string) => console.log(chalk.red(text)); 19 | 20 | /** 21 | * Similar to runThirdPartyLicenses but only for one package. 22 | * 23 | * @param cwd 24 | */ 25 | function runThirdPartyLicenseForPackage(cwd: string) { 26 | logProgress(`Generate 3rd party licenses in ${chalk.underline(cwd)}...`); 27 | execa.sync("yarn", ["grunt", "composer:disclaimer"], { cwd }); 28 | execa.sync("yarn", ["grunt", "yarn:disclaimer"], { cwd }); 29 | logSuccess("Successfully generated 3rd party licenses"); 30 | } 31 | 32 | /** 33 | * Generate a LICENSE file for a given package.json. 34 | * 35 | * @param cwd 36 | * @param author 37 | * @param description 38 | */ 39 | function generateLicenseFile(cwd: string, author: string, description: string) { 40 | const licensePath = resolve(cwd, "LICENSE"); 41 | logProgress(`Generate LICENSE file in ${chalk.underline(licensePath)}...`); 42 | const licenseFile = readFileSync(licensePath, { encoding: DEFAULT_ENCODING }) 43 | .split("\n") 44 | .slice(2) 45 | .join("\n"); 46 | const year = new Date().getFullYear(); 47 | const header = `${description} 48 | Copyright (C) ${year} ${author} 49 | `; 50 | writeFileSync(licensePath, header + licenseFile); 51 | logSuccess("Successfully generated LICENSE file"); 52 | 53 | return licensePath; 54 | } 55 | 56 | /** 57 | * Get a valid workspace package.json content. 58 | * 59 | * @param cwd 60 | * @returns package.json content of the root 61 | * @throws 62 | */ 63 | function getValidWorkspace(cwd: string) { 64 | const json = require(join(cwd, "package.json")); // eslint-disable-line @typescript-eslint/no-var-requires 65 | logSuccess(`Successfully found ${chalk.underline(json.name)} as root project!`); 66 | 67 | if (!json.private) { 68 | throw new Error("This project is not private. Yarn root workspaces must be private!"); 69 | } 70 | 71 | if (!json.workspaces) { 72 | throw new Error("This project has no workspaces defined."); 73 | } 74 | 75 | if (JSON.stringify(json.workspaces).indexOf("plugins/*") === -1) { 76 | throw new Error("This project has no plugins/* workspaces defined."); 77 | } 78 | 79 | return json; 80 | } 81 | 82 | /** 83 | * Get CLI arguments if SKIP_PROMPT is set so the prompt is skipped. 84 | * This is for development purposes only! 85 | * 86 | * @param type 87 | * @private 88 | */ 89 | function getInternalExampleArgs(type: "workspace" | "plugin" | "package") { 90 | if (process.env.SKIP_PROMPT) { 91 | switch (type) { 92 | case "workspace": 93 | return { 94 | checkout: "feat/multipackage", 95 | portPma: 9099, 96 | portWp: 9098, 97 | workspace: "my-awesomeness" 98 | } as CreateWorkspaceOpts; 99 | case "plugin": 100 | return { 101 | author: "Jon Smith", 102 | authorUri: "https://example.com", 103 | constantPrefix: "AWEONE", 104 | dbPrefix: "aweone", 105 | minPhp: "7.0.0", 106 | minWp: "5.2.0", 107 | namespace: "JonSmith\\AwesomeOne", 108 | optPrefix: "awe", 109 | pluginDesc: "This is an awesome plugin #1!", 110 | pluginName: "Awesome One", 111 | pluginUri: "https://awesome-one.example.com", 112 | pluginVersion: "1.0.0", 113 | slug: "awesome-one" 114 | } as CreatePluginOpts; 115 | case "package": 116 | return { 117 | abbreviation: "mbp", 118 | author: "Jon Smith", 119 | namespace: "JonSmith\\MyBestPackage", 120 | packageDesc: "This is an awesome package?!", 121 | packageName: "my-best-package", 122 | packageUri: "https://my-best-package.example.com" 123 | } as CreatePackageOpts; 124 | } 125 | } 126 | return null; 127 | } 128 | 129 | /** 130 | * Get an option from a command by long definition. 131 | * 132 | * @param c 133 | * @param long 134 | */ 135 | function getCommandOption(c: ReturnType, long: Option["long"]) { 136 | return (c.options as Option[]).filter((o) => o.long === long)[0]; 137 | } 138 | 139 | /** 140 | * Used in prompts so you do not have to duplicate the question strings 141 | * and take it directly from the commander option description. 142 | * 143 | * @param c 144 | * @param long 145 | */ 146 | function getCommandDescriptionForPrompt(c: ReturnType, long: Option["long"]) { 147 | return getCommandOption(c, long).description; 148 | } 149 | 150 | /** 151 | * Search and replace file content and write it back with success message. 152 | * 153 | * @param files Absolute pathes 154 | * @param search 155 | * @param replace 156 | */ 157 | function searchAndReplace(files: string[], search: RegExp, replace: any) { 158 | let wroteHeader = false; 159 | files.forEach((file) => { 160 | if (file.indexOf(FOLDER_CWRA) === -1) { 161 | let i = 0; 162 | const newContent = readFileSync(file, { encoding: DEFAULT_ENCODING }).replace(search, () => { 163 | i++; 164 | return replace; 165 | }); 166 | writeFileSync(file, newContent, { encoding: DEFAULT_ENCODING }); 167 | 168 | if (i > 0) { 169 | if (!wroteHeader) { 170 | logSuccess(`Search (${search}) & Replace (${replace}):`); 171 | wroteHeader = true; 172 | } 173 | logSuccess(`├── ${chalk.underline(file)} (${i} times)`); 174 | } 175 | } 176 | }); 177 | } 178 | 179 | /** 180 | * Return an error message when the given input is empty. 181 | * 182 | * @param value 183 | * @returns 184 | */ 185 | function inquirerRequiredValidate(value: string) { 186 | if (!value) { 187 | return "This prompt may not be empty!"; 188 | } 189 | return true; 190 | } 191 | 192 | /** 193 | * Adjust the cases by keys in a given object. 194 | * 195 | * @param object 196 | * @param upper 197 | * @param lower 198 | */ 199 | function caseAll(object: T, upper: Array, lower: Array): T { 200 | upper.forEach((i) => (object[i] = object[i].toUpperCase())); 201 | lower.forEach((i) => (object[i] = object[i].toLowerCase())); 202 | return object; 203 | } 204 | 205 | /** 206 | * Get a git config by parameter. 207 | * 208 | * @param param 209 | * @returns 210 | */ 211 | function getGitConfig(param: string) { 212 | try { 213 | return execa.sync("git", ["config", "--get", param]).stdout; 214 | } catch (e) { 215 | return ""; 216 | } 217 | } 218 | 219 | /** 220 | * Convert a slug like "my-plugin" to "myPlugin". 221 | * 222 | * @param slug 223 | * @param firstUc 224 | * @returns 225 | */ 226 | function slugCamelCase(slug: string, firstUc = false) { 227 | const result = slug.replace(/-([a-z])/g, (g) => g[1].toUpperCase()); 228 | return firstUc ? result.charAt(0).toUpperCase() + result.slice(1) : result; 229 | } 230 | 231 | /** 232 | * Checks if a given version is a valid semver. 233 | * 234 | * @param version 235 | * @param errorAsString 236 | * @returns 237 | * @see https://github.com/semver/semver/issues/232 238 | */ 239 | function isValidSemver(version: string, errorAsString = false) { 240 | const valid = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$/.test( 241 | version 242 | ); 243 | return valid ? true : errorAsString ? "This is not a valid version." : false; 244 | } 245 | 246 | /** 247 | * Checks if a given name is a valid PHP namespace. 248 | * 249 | * @param name 250 | * @returns 251 | */ 252 | function isValidPhpNamespace(name: string) { 253 | return /^[^\\0-9][A-Za-z0-9_\\]+$/.test(name); 254 | } 255 | 256 | /** 257 | * Checks if the given url is valid. 258 | * 259 | * @param url 260 | * @param errorAsString 261 | * @returns 262 | */ 263 | function isValidUrl(url: string, errorAsString = false) { 264 | try { 265 | new URL(url); 266 | return true; 267 | } catch (e) { 268 | return errorAsString ? "This is not a valid URL" : false; 269 | } 270 | } 271 | 272 | /** 273 | * Check for valid workspace and exit if not found. 274 | * 275 | * @param createWorkspaceCwd 276 | * @returns 277 | */ 278 | function checkValidWorkspace(createWorkspaceCwd: string, outputHelp: Command) { 279 | // Get the root package 280 | try { 281 | const root = getValidWorkspace(createWorkspaceCwd); 282 | 283 | if (!existsSync(resolve(createWorkspaceCwd, FOLDER_CWRA, "template"))) { 284 | throw new Error("The template folder in common/create-wp-react-app could not be found!"); 285 | } 286 | return root; 287 | } catch (e) { 288 | logError(e.toString()); 289 | logError( 290 | `You are not using the command inside a folder which was created with ${chalk.underline( 291 | "create-wp-react-app create-workspace" 292 | )}. Navigate to that folder or use the ${chalk.underline("--cwd")} argument.` 293 | ); 294 | outputHelp.help(); 295 | } 296 | } 297 | 298 | export { 299 | logProgress, 300 | logSuccess, 301 | logError, 302 | runThirdPartyLicenseForPackage, 303 | generateLicenseFile, 304 | FOLDER_CWRA, 305 | DEFAULT_ENCODING, 306 | getInternalExampleArgs, 307 | getValidWorkspace, 308 | getCommandOption, 309 | getCommandDescriptionForPrompt, 310 | searchAndReplace, 311 | inquirerRequiredValidate, 312 | caseAll, 313 | getGitConfig, 314 | slugCamelCase, 315 | isValidSemver, 316 | isValidPhpNamespace, 317 | isValidUrl, 318 | checkValidWorkspace 319 | }; 320 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | /* Documentation: https://www.typescriptlang.org/docs/handbook/compiler-options.html */ 3 | "compilerOptions": { 4 | /* Basic options */ 5 | "target": "es5", 6 | "module": "commonjs", 7 | "lib": ["es2015", "dom", "dom.iterable", "esnext"], 8 | "allowJs": false, 9 | "jsx": "preserve", 10 | "skipLibCheck": false, 11 | "isolatedModules": false, 12 | "outDir": "lib", 13 | 14 | /* Strict Type-Checking Options */ 15 | "strict": true, 16 | "noImplicitAny": true, 17 | "strictNullChecks": false, 18 | "strictFunctionTypes": true, 19 | "strictPropertyInitialization": false, 20 | "noImplicitThis": true, 21 | "alwaysStrict": true, 22 | 23 | /* Additional Checks */ 24 | "noUnusedLocals": false, 25 | "noUnusedParameters": false, 26 | "noImplicitReturns": true, 27 | "noFallthroughCasesInSwitch": false, 28 | 29 | /* Module Resolution Options */ 30 | "moduleResolution": "node", 31 | "allowSyntheticDefaultImports": true, 32 | "esModuleInterop": true, 33 | 34 | /* Experimental Options */ 35 | "experimentalDecorators": true 36 | }, 37 | "include": ["src/**/*"] 38 | } 39 | --------------------------------------------------------------------------------