@elementor/wp-lite-env 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
package/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":""}
package/index.js ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { cleanup, commandMap, generateFiles, getCliCommand, getConfigFilePath, getPort } from './src/run';
3
+ const command = process.argv[2];
4
+ if (!commandMap[command]) {
5
+ console.log(`Valid commands: ${Object.keys(commandMap).join(', ')}. You used ${command}`);
6
+ }
7
+ const port = getPort(process.argv);
8
+ const configFilePath = getConfigFilePath(process.argv);
9
+ const runPath = generateFiles(port, configFilePath);
10
+ const cliCommand = getCliCommand(process.argv);
11
+ try {
12
+ await commandMap[command](port, runPath, cliCommand);
13
+ }
14
+ finally {
15
+ cleanup(port, runPath);
16
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@elementor/wp-lite-env",
3
+ "version": "0.0.6",
4
+ "private": false,
5
+ "description": "A simple, lightweight, docker-based WordPress environment",
6
+ "main": "dist/index.js",
7
+ "type": "module",
8
+ "types": "dist/index.d.ts",
9
+ "bin": {
10
+ "wp-lite-env": "dist/index.js"
11
+ },
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "lint": "eslint",
15
+ "release": "npm run build && changeset publish",
16
+ "test": "jest --coverage=true"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/elementor/wp-lite-env.git"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public",
24
+ "directory": "dist"
25
+ },
26
+ "author": "Elementor Team",
27
+ "license": "ISC",
28
+ "bugs": {
29
+ "url": "https://github.com/elementor/wp-lite-env/issues"
30
+ },
31
+ "homepage": "https://github.com/elementor/wp-lite-env#readme",
32
+ "dependencies": {
33
+ "docker-compose": "^1.1.0"
34
+ },
35
+ "devDependencies": {
36
+ "@changesets/cli": "^2.27.9",
37
+ "@eslint/js": "^9.15.0",
38
+ "@jest/globals": "^29.7.0",
39
+ "@types/eslint__js": "^8.42.3",
40
+ "@types/node": "^22.9.0",
41
+ "eslint": "~9.14.0",
42
+ "jest": "^29.7.0",
43
+ "ts-jest": "^29.2.5",
44
+ "typescript": "^5.6.3",
45
+ "typescript-eslint": "^8.14.0"
46
+ }
47
+ }
@@ -0,0 +1,16 @@
1
+ export type Config = {
2
+ core?: string;
3
+ phpVersion?: string;
4
+ plugins?: {
5
+ [key: string]: string;
6
+ };
7
+ themes?: {
8
+ [key: string]: string;
9
+ };
10
+ mappings?: {
11
+ [key: string]: string;
12
+ };
13
+ config?: Record<string, string | boolean>;
14
+ };
15
+ export declare const getConfig: (configFilePath?: string) => Config;
16
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,MAAM,GAAG;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACpC,MAAM,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACnC,QAAQ,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAC,OAAO,CAAC,CAAC;CACxC,CAAA;AAED,eAAO,MAAM,SAAS,oBAAsB,MAAM,KAAI,MAuBrD,CAAC"}
package/src/config.js ADDED
@@ -0,0 +1,23 @@
1
+ import fs from 'fs';
2
+ export const getConfig = (configFilePath) => {
3
+ let configFile = {};
4
+ if (configFilePath) {
5
+ configFile = JSON.parse(fs.readFileSync(configFilePath, 'utf8'));
6
+ }
7
+ const defaultConfig = {
8
+ core: '6.7',
9
+ phpVersion: '8.1',
10
+ plugins: {},
11
+ themes: {},
12
+ mappings: {},
13
+ config: {},
14
+ };
15
+ return {
16
+ core: configFile.core || defaultConfig.core,
17
+ phpVersion: configFile.phpVersion || defaultConfig.phpVersion,
18
+ plugins: configFile.plugins || defaultConfig.plugins,
19
+ themes: configFile.themes || defaultConfig.themes,
20
+ mappings: configFile.mappings || defaultConfig.mappings,
21
+ config: configFile.config || defaultConfig.config,
22
+ };
23
+ };
package/src/run.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ export declare const start: (port: string, runPath: string) => Promise<void>;
2
+ export declare const stop: (port: string, runPath: string) => Promise<void>;
3
+ export declare const commandMap: {
4
+ [key: string]: ((port: string) => Promise<void>) | ((port: string, runPath: string, command: string) => Promise<void>);
5
+ };
6
+ export declare const generateFiles: (port: string, configFilePath: string) => string;
7
+ export declare const getConfigFilePath: (processArgs: string[]) => string;
8
+ export declare const getCliCommand: (processArgs: string[]) => string;
9
+ export declare const getPort: (processArgs: string[]) => string;
10
+ export declare const cleanup: (port: string, runPath: string) => void;
11
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/run.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,KAAK,SAAiB,MAAM,WAAW,MAAM,kBASzD,CAAC;AAEF,eAAO,MAAM,IAAI,SAAiB,MAAM,WAAW,MAAM,kBAOxD,CAAC;AAWF,eAAO,MAAM,UAAU,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,CAAE,CAAE,IAAI,EAAE,MAAM,KAAM,OAAO,CAAC,IAAI,CAAC,CAAE,GAAG,CAAE,CAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAM,OAAO,CAAC,IAAI,CAAC,CAAE,CAAA;CAIxJ,CAAC;AAIF,eAAO,MAAM,aAAa,SAAW,MAAM,kBAAkB,MAAM,WA4BlE,CAAC;AAYF,eAAO,MAAM,iBAAiB,gBAAkB,MAAM,EAAE,WAEvD,CAAC;AAEF,eAAO,MAAM,aAAa,gBAAkB,MAAM,EAAE,WAEnD,CAAC;AAEF,eAAO,MAAM,OAAO,gBAAkB,MAAM,EAAE,WAE7C,CAAC;AAEF,eAAO,MAAM,OAAO,SAAW,MAAM,WAAW,MAAM,SAGrD,CAAA"}
package/src/run.js ADDED
@@ -0,0 +1,105 @@
1
+ import { downAll, run, upAll } from "docker-compose";
2
+ import path from "path";
3
+ import { getConfig } from "./config";
4
+ import fs from "fs";
5
+ import { generateCliDockerfileTemplate, generateConfiguration, generateDockerComposeYmlTemplate, generateWordPressDockerfileTemplate } from "./templates";
6
+ import { createHash } from "crypto";
7
+ import os from "node:os";
8
+ const waitForServer = async (url, timeoutMs) => {
9
+ const startTime = Date.now();
10
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
11
+ while (startTime + timeoutMs > Date.now()) {
12
+ try {
13
+ const response = await fetch(url);
14
+ if (response.ok && (200 === response.status || 302 === response.status)) {
15
+ return true;
16
+ }
17
+ }
18
+ catch (e) { // eslint-disable-line @typescript-eslint/no-unused-vars
19
+ // Ignore
20
+ }
21
+ finally {
22
+ await sleep(100);
23
+ }
24
+ }
25
+ return false;
26
+ };
27
+ export const start = async (port, runPath) => {
28
+ await upAll({
29
+ commandOptions: ['--build'],
30
+ composeOptions: ['-p', `port${port}`],
31
+ cwd: runPath,
32
+ log: true,
33
+ });
34
+ await waitForServer(`http://localhost:${port}`, 10000);
35
+ await cli(port, runPath, 'bash wp-config/configure-wp.sh');
36
+ };
37
+ export const stop = async (port, runPath) => {
38
+ await downAll({
39
+ cwd: runPath,
40
+ commandOptions: ['--volumes', '--remove-orphans'],
41
+ composeOptions: ['-p', `port${port}`],
42
+ log: true,
43
+ });
44
+ };
45
+ const cli = async (port, runPath, command) => {
46
+ await run('cli', command, {
47
+ cwd: runPath,
48
+ commandOptions: ['--rm'],
49
+ composeOptions: ['-p', `port${port}`],
50
+ log: true,
51
+ });
52
+ };
53
+ export const commandMap = {
54
+ start,
55
+ stop,
56
+ cli,
57
+ };
58
+ const getWpConfigPath = (port) => path.resolve(process.cwd(), port);
59
+ export const generateFiles = (port, configFilePath) => {
60
+ const config = getConfig(configFilePath);
61
+ // Using a local path since Docker Compose cannot access /tmp
62
+ // See: https://github.com/docker/compose/issues/1153
63
+ const wpConfigPath = getWpConfigPath(port);
64
+ if (!fs.existsSync(wpConfigPath)) {
65
+ fs.mkdirSync(wpConfigPath, { recursive: true });
66
+ }
67
+ const wpConfig = generateConfiguration(config, port);
68
+ fs.writeFileSync(path.resolve(wpConfigPath, 'configure-wp.sh'), wpConfig);
69
+ const dockerComposeYmlTemplate = generateDockerComposeYmlTemplate(config, process.cwd(), port, wpConfigPath);
70
+ const wordPressDockerfileTemplate = generateWordPressDockerfileTemplate(config);
71
+ const cliDockerfileTemplate = generateCliDockerfileTemplate(config);
72
+ const hash = createHash('sha256');
73
+ hash.update(dockerComposeYmlTemplate + wordPressDockerfileTemplate + cliDockerfileTemplate + port);
74
+ const runPath = path.resolve(os.tmpdir(), `${hash.digest('hex')}`);
75
+ if (!fs.existsSync(runPath)) {
76
+ fs.mkdirSync(runPath);
77
+ }
78
+ console.log(`writing files to run path: ${runPath}`);
79
+ fs.writeFileSync(path.resolve(runPath, 'docker-compose.yml'), dockerComposeYmlTemplate);
80
+ fs.writeFileSync(path.resolve(runPath, 'WordPress.Dockerfile'), wordPressDockerfileTemplate);
81
+ fs.writeFileSync(path.resolve(runPath, 'CLI.Dockerfile'), cliDockerfileTemplate);
82
+ return runPath;
83
+ };
84
+ const getArgument = (argumentKey, processArgs) => {
85
+ for (let i = 3; i < processArgs.length; i++) {
86
+ const argument = processArgs[i];
87
+ if (argument.startsWith(`${argumentKey}=`)) {
88
+ return argument.substring(argumentKey.length + 1);
89
+ }
90
+ }
91
+ return undefined;
92
+ };
93
+ export const getConfigFilePath = (processArgs) => {
94
+ return path.resolve(getArgument('config', processArgs));
95
+ };
96
+ export const getCliCommand = (processArgs) => {
97
+ return getArgument('command', processArgs);
98
+ };
99
+ export const getPort = (processArgs) => {
100
+ return getArgument('port', processArgs) || '8888';
101
+ };
102
+ export const cleanup = (port, runPath) => {
103
+ fs.rmSync(getWpConfigPath(port), { recursive: true, force: true });
104
+ fs.rmSync(runPath, { recursive: true, force: true });
105
+ };
@@ -0,0 +1,6 @@
1
+ import { Config } from './config';
2
+ export declare const generateDockerComposeYmlTemplate: (config: Config, basePath: string, port: string, configPath: string) => string;
3
+ export declare const generateWordPressDockerfileTemplate: (config: Config) => string;
4
+ export declare const generateCliDockerfileTemplate: (config: Config) => string;
5
+ export declare const generateConfiguration: (config: Config, port: string) => string;
6
+ //# sourceMappingURL=templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGlC,eAAO,MAAM,gCAAgC,WAAa,MAAM,YAAY,MAAM,QAAQ,MAAM,cAAc,MAAM,WA0EnH,CAAC;AAEF,eAAO,MAAM,mCAAmC,WAAa,MAAM,WASlE,CAAC;AAEF,eAAO,MAAM,6BAA6B,WAAa,MAAM,WAa5D,CAAC;AAEF,eAAO,MAAM,qBAAqB,WAAa,MAAM,QAAQ,MAAM,WAWlE,CAAC"}
@@ -0,0 +1,111 @@
1
+ import path from 'path';
2
+ export const generateDockerComposeYmlTemplate = (config, basePath, port, configPath) => {
3
+ const mappingsStringArray = Object.keys(config.mappings).map((key) => {
4
+ const value = config.mappings[key];
5
+ return ` - >-
6
+ ${path.resolve(basePath, value)}:/var/www/html/${key}\n`;
7
+ });
8
+ const pluginsStringArray = Object.keys(config.plugins).map((key) => {
9
+ const value = config.plugins[key];
10
+ return ` - >-
11
+ ${path.resolve(basePath, value)}:/var/www/html/wp-content/plugins/${key}\n`;
12
+ });
13
+ const themesStringArray = Object.keys(config.themes).map((key) => {
14
+ const value = config.themes[key];
15
+ return ` - >-
16
+ ${path.resolve(basePath, value)}:/var/www/html/wp-content/themes/${key}\n`;
17
+ });
18
+ const wpContent = ` - >-
19
+ wpcontent:/var/www/html\n`;
20
+ const wpConfig = ` - >-
21
+ ${configPath}:/var/www/html/wp-config\n`;
22
+ const volumes = mappingsStringArray.concat(pluginsStringArray).concat(themesStringArray).concat([wpContent, wpConfig]).join('');
23
+ return `services:
24
+ mysql:
25
+ image: 'mariadb:lts'
26
+ ports:
27
+ - '\${WP_ENV_MYSQL_PORT:-}:3306'
28
+ environment:
29
+ MYSQL_ROOT_HOST: '%'
30
+ MYSQL_ROOT_PASSWORD: password
31
+ MYSQL_DATABASE: wordpress
32
+ volumes:
33
+ - 'mysql:/var/lib/mysql'
34
+ wordpress:
35
+ depends_on:
36
+ - mysql
37
+ build:
38
+ context: .
39
+ dockerfile: WordPress.Dockerfile
40
+ no_cache: true
41
+ args: &ref_0
42
+ HOST_USERNAME: yotams
43
+ HOST_UID: '502'
44
+ HOST_GID: '20'
45
+ ports:
46
+ - '\${WP_ENV_PORT:-${port}}:80'
47
+ environment:
48
+ APACHE_RUN_USER: '#502'
49
+ APACHE_RUN_GROUP: '#20'
50
+ WORDPRESS_DB_USER: root
51
+ WORDPRESS_DB_PASSWORD: password
52
+ WORDPRESS_DB_NAME: wordpress
53
+ volumes: &ref_1
54
+ ${volumes}
55
+ extra_hosts:
56
+ - 'host.docker.internal:host-gateway'
57
+ cli:
58
+ depends_on:
59
+ - wordpress
60
+ build:
61
+ context: .
62
+ dockerfile: CLI.Dockerfile
63
+ args: *ref_0
64
+ volumes: *ref_1
65
+ user: '502:20'
66
+ environment:
67
+ WORDPRESS_DB_USER: root
68
+ WORDPRESS_DB_PASSWORD: password
69
+ WORDPRESS_DB_NAME: wordpress
70
+ extra_hosts:
71
+ - 'host.docker.internal:host-gateway'
72
+ volumes:
73
+ mysql: {}
74
+ wpcontent: {}
75
+ `;
76
+ };
77
+ export const generateWordPressDockerfileTemplate = (config) => {
78
+ return `FROM wordpress:${config.core}-php${config.phpVersion}
79
+ ARG HOST_USERNAME
80
+ ARG HOST_UID
81
+ ARG HOST_GID
82
+ # When the IDs are already in use we can still safely move on.
83
+ RUN groupadd -o -g $HOST_GID $HOST_USERNAME || true
84
+ RUN useradd -mlo -u $HOST_UID -g $HOST_GID $HOST_USERNAME || true
85
+ `;
86
+ };
87
+ export const generateCliDockerfileTemplate = (config) => {
88
+ return `FROM wordpress:cli-php${config.phpVersion}
89
+ ARG HOST_USERNAME
90
+ ARG HOST_UID
91
+ ARG HOST_GID
92
+ # When the IDs are already in use we can still safely move on.
93
+ RUN addgroup -g $HOST_GID $HOST_USERNAME || true
94
+ RUN useradd -mlo -u $HOST_UID -g $HOST_GID $HOST_USERNAME || true
95
+ # RUN adduser -h /home/$HOST_USERNAME -G $( getent group $HOST_GID | cut -d: -f1 ) -u $HOST_UID $HOST_USERNAME || true
96
+
97
+ # Have the container sleep infinitely to keep it alive for us to run commands on it.
98
+ CMD [ "/bin/sh", "-c", "while true; do sleep 2073600; done" ]
99
+ `;
100
+ };
101
+ export const generateConfiguration = (config, port) => {
102
+ const header = `#!/bin/bash
103
+ set -eox pipefail
104
+ `;
105
+ const configStringArray = Object.keys(config.config).map((key) => {
106
+ const value = config.config[key];
107
+ return `wp config set ${key} ${value} --raw`;
108
+ });
109
+ const wpCoreInstall = `wp core install --url="http://localhost:${port}" --title="test" --admin_user=admin --admin_password=password --admin_email=wordpress@example.com --skip-email`;
110
+ return [header, wpCoreInstall].concat(configStringArray).join('\n');
111
+ };