├── .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 |
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 | [](https://github.com/devowlio/wp-react-starter)
12 | [](https://matthias-web.com/slack)
13 | [](https://codecov.io/gl/devowlio/wp-reactjs-starter)
14 | [](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 |
--------------------------------------------------------------------------------