wp-advads 1.0.33 → 1.0.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/test.yml +17 -0
- package/CHANGELOG.md +23 -1
- package/LICENSE +15 -0
- package/README.md +86 -66
- package/package.json +9 -7
- package/src/app.js +17 -11
- package/src/commands/build.js +72 -59
- package/src/commands/class.js +34 -40
- package/src/commands/css.js +13 -19
- package/src/commands/init.js +4 -1
- package/src/commands/javascript.js +13 -19
- package/src/commands/release/change-version.js +14 -17
- package/src/commands/release/changelog.js +23 -31
- package/src/commands/release/git.js +25 -11
- package/src/commands/release/github.js +63 -12
- package/src/commands/release/semver.js +22 -25
- package/src/commands/release/translations.js +17 -23
- package/src/commands/release.js +3 -4
- package/src/commands/updates.js +20 -17
- package/src/commands/views.js +16 -18
- package/src/commands/wp-glotpress.js +125 -126
- package/src/commands/wp-pot.js +17 -9
- package/src/utilities/filesystem.js +57 -36
- package/src/utilities/flow.js +21 -0
- package/src/utilities/formatting.js +13 -4
- package/src/utilities/index.js +1 -1
- package/src/utilities/misc.js +1 -6
- package/src/utilities/settings.js +34 -14
- package/src/utilities/shell.js +60 -8
- package/template/files/class-singleton.php +5 -0
- package/template/files/javascript.js +0 -2
- package/template/files/view.php +2 -0
- package/test/build.test.js +168 -0
- package/test/changelog-locales.test.js +49 -0
- package/test/class.test.js +47 -0
- package/test/compile-templates.test.js +70 -0
- package/test/deps-hygiene.test.js +46 -0
- package/test/engines.test.js +11 -0
- package/test/get-latest-tag.test.js +51 -0
- package/test/git-precheck.test.js +44 -0
- package/test/github-final.test.js +93 -0
- package/test/glotpress-download.test.js +176 -0
- package/test/nested-path.test.js +39 -0
- package/test/no-fs-extra.test.js +11 -0
- package/test/package-identity.test.js +19 -0
- package/test/path-confine.test.js +42 -0
- package/test/pot-docs.test.js +27 -0
- package/test/pot-headers.test.js +49 -0
- package/test/project-root.test.js +26 -0
- package/test/release-waterfall.test.js +62 -0
- package/test/semver.test.js +25 -0
- package/test/settings-clone.test.js +34 -0
- package/test/settings-path.test.js +31 -0
- package/test/shell.test.js +100 -0
- package/test/singleton.test.js +14 -0
- package/test/stable-tag.test.js +13 -0
- package/test/stub-security.test.js +37 -0
- package/test/template-folder.test.js +19 -0
- package/test/translations.test.js +51 -0
- package/test/update-file-content.test.js +38 -0
- package/test/updates.test.js +39 -0
- package/test/version.test.js +22 -0
- package/test/view-escape.test.js +17 -0
- package/test/view.test.js +33 -0
- package/test/write-force.test.js +47 -0
- package/src/utilities/semver.js +0 -45
- package/template/.github/pull_request_template.md +0 -52
- package/template/configs/.editorconfig +0 -27
- package/template/configs/.eslintignore +0 -13
- package/template/configs/.eslintrc.js +0 -13
- package/template/configs/.phpcs.xml.dist +0 -87
- package/template/configs/.prettierignore +0 -49
- package/template/configs/.prettierrc.js +0 -3
- package/template/configs/.stylelintrc +0 -44
- package/template/configs/composer.json +0 -51
- package/template/configs/gitattributes +0 -34
- package/template/configs/gitignore +0 -43
- package/template/configs/package.json +0 -66
- package/template/configs/postcss.config.js +0 -6
- package/template/configs/tailwind.config.js +0 -8
- package/template/configs/webpack.mix.js +0 -65
- package/template/configs/webpack.mix.local.js +0 -3
- package/template/includes/index.php +0 -2
- package/template/index.js +0 -0
- package/template/index.php +0 -4
- package/template/plugin/assets/scss/app.scss +0 -0
- package/template/plugin/assets/src/app.js +0 -0
- package/template/tools/laravel-mix/wp-pot.js +0 -57
- package/template/tools/wp-glotpress.js +0 -162
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import chalk from 'chalk';
|
|
5
4
|
import logSymbols from 'log-symbols';
|
|
6
5
|
import template from 'lodash/template.js';
|
|
7
6
|
|
|
@@ -9,12 +8,13 @@ import template from 'lodash/template.js';
|
|
|
9
8
|
* Node dependencies
|
|
10
9
|
*/
|
|
11
10
|
import path from 'path';
|
|
12
|
-
import fs from 'fs
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Internal Dependencies
|
|
16
16
|
*/
|
|
17
|
-
import { msgSuccessOnSameLine, onSameLine, write } from './formatting.js'
|
|
17
|
+
import { msgErrorTitle, msgSuccessOnSameLine, onSameLine, write } from './formatting.js'
|
|
18
18
|
|
|
19
19
|
let projectRoot = null
|
|
20
20
|
|
|
@@ -32,32 +32,26 @@ export function getProjectRoot(startDir = process.cwd()) {
|
|
|
32
32
|
return projectRoot;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
let dir = startDir;
|
|
36
36
|
|
|
37
|
-
while (
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
while (dir !== path.parse(dir).root) {
|
|
38
|
+
if (fs.existsSync(path.join(dir, 'wp.advads'))) {
|
|
39
|
+
projectRoot = dir;
|
|
40
40
|
return projectRoot;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
43
|
+
dir = path.dirname(dir);
|
|
44
|
+
}
|
|
46
45
|
|
|
47
|
-
|
|
46
|
+
projectRoot = null;
|
|
47
|
+
throw new Error('Project root not found. Make sure there is a wp.advads file in the root directory.');
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
* Retrieves the path to the template folder.
|
|
52
|
-
*
|
|
53
|
-
* This function uses `createRequire` to resolve the path to the `../../template` directory
|
|
54
|
-
* relative to the current module's URL and removes the `/index.js` part from the resolved path.
|
|
55
|
-
*
|
|
56
51
|
* @returns {string} The path to the template folder.
|
|
57
52
|
*/
|
|
58
53
|
export function getTemplateFolder() {
|
|
59
|
-
|
|
60
|
-
return path.join(dirname, '../../template')
|
|
54
|
+
return path.join( path.dirname( fileURLToPath( import.meta.url ) ), '../../template' )
|
|
61
55
|
}
|
|
62
56
|
|
|
63
57
|
/**
|
|
@@ -87,21 +81,22 @@ export function compileTemplate( filename, data ) {
|
|
|
87
81
|
}
|
|
88
82
|
|
|
89
83
|
/**
|
|
90
|
-
*
|
|
84
|
+
* Compile a template and write it, or print the shared scaffold failure.
|
|
91
85
|
*
|
|
92
|
-
* @param {string}
|
|
93
|
-
* @param {string}
|
|
94
|
-
*
|
|
95
|
-
* @
|
|
86
|
+
* @param {string} folder
|
|
87
|
+
* @param {string} filename
|
|
88
|
+
* @param {string} templateFile Path under template/
|
|
89
|
+
* @param {Object} data
|
|
90
|
+
* @param {boolean} force
|
|
96
91
|
*/
|
|
97
|
-
export function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
92
|
+
export function writeFromTemplate( folder, filename, templateFile, data, force = false ) {
|
|
93
|
+
try {
|
|
94
|
+
const content = compileTemplate( getTemplateFile( templateFile ), data )
|
|
95
|
+
writeFile( folder, filename, content, force )
|
|
96
|
+
} catch ( err ) {
|
|
97
|
+
msgErrorTitle( 'We failed!!!' )
|
|
98
|
+
throw err
|
|
99
|
+
}
|
|
105
100
|
}
|
|
106
101
|
|
|
107
102
|
/**
|
|
@@ -120,7 +115,7 @@ export function updateFileContent(fileName, messages, next, callback) {
|
|
|
120
115
|
fs.readFile(fileName, (err, buffer) => {
|
|
121
116
|
if ( null !== err ) {
|
|
122
117
|
onSameLine(`${logSymbols.error} ${messages.failed}`)
|
|
123
|
-
return next()
|
|
118
|
+
return next(err)
|
|
124
119
|
}
|
|
125
120
|
|
|
126
121
|
const content = callback(buffer.toString())
|
|
@@ -128,7 +123,7 @@ export function updateFileContent(fileName, messages, next, callback) {
|
|
|
128
123
|
fs.writeFile(fileName, content, (err) => {
|
|
129
124
|
if ( null !== err ) {
|
|
130
125
|
onSameLine(`${logSymbols.error} ${messages.failed}`)
|
|
131
|
-
return next()
|
|
126
|
+
return next(err)
|
|
132
127
|
}
|
|
133
128
|
|
|
134
129
|
onSameLine(`${logSymbols.success} ${messages.updated}`)
|
|
@@ -137,12 +132,38 @@ export function updateFileContent(fileName, messages, next, callback) {
|
|
|
137
132
|
})
|
|
138
133
|
}
|
|
139
134
|
|
|
140
|
-
|
|
135
|
+
function assertInsideProject( target ) {
|
|
136
|
+
if ( String( target ).includes( '\0' ) ) {
|
|
137
|
+
throw new Error( 'Invalid path' )
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const root = path.resolve( getProjectRoot() )
|
|
141
|
+
const resolved = path.resolve( target )
|
|
142
|
+
const relative = path.relative( root, resolved )
|
|
143
|
+
|
|
144
|
+
if ( relative.startsWith( `..${path.sep}` ) || relative === '..' || path.isAbsolute( relative ) ) {
|
|
145
|
+
throw new Error( 'Path escapes project root' )
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function writeFile(folder, filename, content, force = false) {
|
|
150
|
+
if ( String( filename ).includes( '\0' ) ) {
|
|
151
|
+
throw new Error( 'Invalid path' )
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
assertInsideProject( folder )
|
|
155
|
+
assertInsideProject( path.join( folder, filename ) )
|
|
156
|
+
|
|
157
|
+
const target = path.join( folder, filename )
|
|
158
|
+
if ( ! force && fs.existsSync( target ) ) {
|
|
159
|
+
throw new Error( `File exists: ${target}` )
|
|
160
|
+
}
|
|
161
|
+
|
|
141
162
|
write('Creating directories!!');
|
|
142
|
-
fs.
|
|
163
|
+
fs.mkdirSync(folder, { recursive: true });
|
|
143
164
|
msgSuccessOnSameLine('Directories created successfully');
|
|
144
165
|
|
|
145
166
|
write('Creating file!!');
|
|
146
|
-
fs.writeFileSync(
|
|
167
|
+
fs.writeFileSync(target, content);
|
|
147
168
|
msgSuccessOnSameLine('File created successfully');
|
|
148
169
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run callback-style steps in order. `next(err)` if any step fails.
|
|
3
|
+
*
|
|
4
|
+
* @param {Function[]} steps Each step is `(done) => void`
|
|
5
|
+
* @param {Function} next
|
|
6
|
+
*/
|
|
7
|
+
export function runSteps( steps, next ) {
|
|
8
|
+
;( async () => {
|
|
9
|
+
for ( const step of steps ) {
|
|
10
|
+
await new Promise( ( resolve, reject ) => {
|
|
11
|
+
step( ( err ) => {
|
|
12
|
+
if ( err ) {
|
|
13
|
+
reject( err )
|
|
14
|
+
} else {
|
|
15
|
+
resolve()
|
|
16
|
+
}
|
|
17
|
+
} )
|
|
18
|
+
} )
|
|
19
|
+
}
|
|
20
|
+
} )().then( () => next() ).catch( ( err ) => next( err ) )
|
|
21
|
+
}
|
|
@@ -28,6 +28,10 @@ export function onNewLine(text) {
|
|
|
28
28
|
console.log(text)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
function canRewriteStdout() {
|
|
32
|
+
return Boolean( process.stdout.isTTY && ! process.env.NODE_TEST_CONTEXT )
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
/**
|
|
32
36
|
* Print text on same line by clearing buffer.
|
|
33
37
|
*
|
|
@@ -36,9 +40,9 @@ export function onNewLine(text) {
|
|
|
36
40
|
* @returns {void}
|
|
37
41
|
*/
|
|
38
42
|
export function onSameLine(text) {
|
|
39
|
-
if (process.stdout.clearLine) {
|
|
40
|
-
process.stdout.clearLine();
|
|
41
|
-
process.stdout.cursorTo(0);
|
|
43
|
+
if ( canRewriteStdout() && process.stdout.clearLine ) {
|
|
44
|
+
process.stdout.clearLine();
|
|
45
|
+
process.stdout.cursorTo(0);
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
console.log(text)
|
|
@@ -118,5 +122,10 @@ export function msgErrorTitle(msg) {
|
|
|
118
122
|
* @returns {void}
|
|
119
123
|
*/
|
|
120
124
|
export function write(text) {
|
|
121
|
-
|
|
125
|
+
if ( canRewriteStdout() ) {
|
|
126
|
+
process.stdout.write(text)
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log(text)
|
|
122
131
|
}
|
package/src/utilities/index.js
CHANGED
package/src/utilities/misc.js
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* External Dependencies
|
|
3
|
-
*/
|
|
4
|
-
import capitalize from 'lodash/capitalize.js';
|
|
5
|
-
|
|
6
1
|
export function filenameToHeading(filename) {
|
|
7
2
|
filename = filename.replace('.php', '');
|
|
8
3
|
filename = filename.replace('.js', '');
|
|
9
4
|
filename = filename.replace(/-/g, ' ');
|
|
10
5
|
|
|
11
|
-
return
|
|
6
|
+
return filename.charAt(0).toUpperCase() + filename.slice(1).toLowerCase();
|
|
12
7
|
}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* External dependencies
|
|
3
|
-
*/
|
|
4
|
-
import get from 'lodash/get.js'
|
|
5
|
-
import set from 'lodash/set.js'
|
|
6
|
-
|
|
7
1
|
/**
|
|
8
2
|
* Node dependencies
|
|
9
3
|
*/
|
|
@@ -18,6 +12,14 @@ import { write } from './formatting.js'
|
|
|
18
12
|
|
|
19
13
|
let settings = null
|
|
20
14
|
|
|
15
|
+
function configPath() {
|
|
16
|
+
try {
|
|
17
|
+
return path.join( getProjectRoot(), 'wp.advads' )
|
|
18
|
+
} catch {
|
|
19
|
+
return path.join( process.cwd(), 'wp.advads' )
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
21
23
|
/**
|
|
22
24
|
* Checks if the configuration file exists at the specified path.
|
|
23
25
|
*
|
|
@@ -25,8 +27,7 @@ let settings = null
|
|
|
25
27
|
*/
|
|
26
28
|
export function configFileExists() {
|
|
27
29
|
try {
|
|
28
|
-
|
|
29
|
-
return fs.existsSync(configPath);
|
|
30
|
+
return fs.existsSync( configPath() );
|
|
30
31
|
} catch (err) {
|
|
31
32
|
return false;
|
|
32
33
|
}
|
|
@@ -39,8 +40,7 @@ export function configFileExists() {
|
|
|
39
40
|
*/
|
|
40
41
|
export function readConfigFile() {
|
|
41
42
|
try {
|
|
42
|
-
const
|
|
43
|
-
const data = fs.readFileSync(configPath, 'utf-8');
|
|
43
|
+
const data = fs.readFileSync( configPath(), 'utf-8' );
|
|
44
44
|
return JSON.parse(data);
|
|
45
45
|
} catch (err) {
|
|
46
46
|
return {};
|
|
@@ -55,9 +55,8 @@ export function readConfigFile() {
|
|
|
55
55
|
export function saveConfig(data) {
|
|
56
56
|
write('Saving config file...');
|
|
57
57
|
try {
|
|
58
|
-
const savePath = path.join(process.cwd(), 'wp.advads')
|
|
59
58
|
const jsonData = JSON.stringify(data, null, 2);
|
|
60
|
-
fs.writeFileSync(
|
|
59
|
+
fs.writeFileSync( configPath(), jsonData );
|
|
61
60
|
settings = data;
|
|
62
61
|
} catch (err) {
|
|
63
62
|
write('Error writing config file:', err);
|
|
@@ -82,7 +81,7 @@ export function getSetting(key = 'all', defaultVal = '') {
|
|
|
82
81
|
return settings;
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
return
|
|
84
|
+
return getPath( settings, key, defaultVal );
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
export function updateSetting(key, value) {
|
|
@@ -90,6 +89,27 @@ export function updateSetting(key, value) {
|
|
|
90
89
|
settings = readConfigFile();
|
|
91
90
|
}
|
|
92
91
|
|
|
93
|
-
settings =
|
|
92
|
+
settings = setPath(settings, key, value);
|
|
94
93
|
saveConfig(settings);
|
|
95
94
|
}
|
|
95
|
+
|
|
96
|
+
function getPath( object, key, defaultVal ) {
|
|
97
|
+
const value = String( key ).split( '.' ).reduce( ( cur, part ) => cur?.[ part ], object )
|
|
98
|
+
return value === undefined ? defaultVal : value
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function setPath( object, key, value ) {
|
|
102
|
+
const parts = String( key ).split( '.' )
|
|
103
|
+
let cur = object
|
|
104
|
+
|
|
105
|
+
for ( let i = 0; i < parts.length - 1; i++ ) {
|
|
106
|
+
const part = parts[ i ]
|
|
107
|
+
if ( cur[ part ] == null || typeof cur[ part ] !== 'object' ) {
|
|
108
|
+
cur[ part ] = {}
|
|
109
|
+
}
|
|
110
|
+
cur = cur[ part ]
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
cur[ parts[ parts.length - 1 ] ] = value
|
|
114
|
+
return object
|
|
115
|
+
}
|
package/src/utilities/shell.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Node dependencies
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { spawn } from 'node:child_process'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Internal Dependencies
|
|
@@ -19,10 +19,20 @@ import { onSameLine } from './formatting.js'
|
|
|
19
19
|
*/
|
|
20
20
|
export function runCommand( command, args, callback ) {
|
|
21
21
|
const commandSpawn = spawn( command, args, {
|
|
22
|
-
shell:
|
|
23
|
-
stdio: [ 'inherit' ]
|
|
22
|
+
shell: false,
|
|
23
|
+
stdio: [ 'inherit', 'pipe', 'pipe' ]
|
|
24
24
|
} )
|
|
25
25
|
|
|
26
|
+
let finished = false
|
|
27
|
+
const done = ( err ) => {
|
|
28
|
+
if ( finished ) {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
finished = true
|
|
33
|
+
callback( err )
|
|
34
|
+
}
|
|
35
|
+
|
|
26
36
|
commandSpawn.stdout.on( 'data', (data) => {
|
|
27
37
|
onSameLine(data.toString())
|
|
28
38
|
})
|
|
@@ -31,23 +41,65 @@ export function runCommand( command, args, callback ) {
|
|
|
31
41
|
onSameLine(data.toString().replace('\n','').replace('\r','').trim())
|
|
32
42
|
})
|
|
33
43
|
|
|
44
|
+
commandSpawn.on( 'error', done )
|
|
45
|
+
|
|
34
46
|
commandSpawn.on( 'close', (code) => {
|
|
35
|
-
|
|
47
|
+
if ( code !== 0 ) {
|
|
48
|
+
done( new Error( `Command failed with exit code ${code}` ) )
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
done()
|
|
36
53
|
})
|
|
37
54
|
}
|
|
38
55
|
|
|
39
56
|
/**
|
|
40
57
|
* Execute a command in the terminal.
|
|
41
58
|
*
|
|
42
|
-
* @param {string} command Command to execute
|
|
59
|
+
* @param {string|string[]} command Command to execute, or argv array
|
|
43
60
|
* @param {Function} callback Callback function
|
|
61
|
+
* @param {Object} options cwd/env for spawn
|
|
44
62
|
*
|
|
45
63
|
* @returns {void}
|
|
46
64
|
*/
|
|
47
65
|
export function execCommand( command, callback, options = {} ) {
|
|
48
|
-
|
|
49
|
-
|
|
66
|
+
const spawnOpts = {
|
|
67
|
+
cwd: options.cwd,
|
|
68
|
+
env: options.env,
|
|
69
|
+
stdio: [ 'ignore', 'pipe', 'pipe' ],
|
|
50
70
|
}
|
|
51
71
|
|
|
52
|
-
|
|
72
|
+
const child = Array.isArray( command )
|
|
73
|
+
? spawn( command[0], command.slice( 1 ), { ...spawnOpts, shell: false } )
|
|
74
|
+
: spawn( command, { ...spawnOpts, shell: true } )
|
|
75
|
+
|
|
76
|
+
let stdout = ''
|
|
77
|
+
let stderr = ''
|
|
78
|
+
let finished = false
|
|
79
|
+
const done = ( error ) => {
|
|
80
|
+
if ( finished ) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
finished = true
|
|
85
|
+
callback( stdout, error, stderr )
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
child.stdout.on( 'data', ( data ) => {
|
|
89
|
+
const text = data.toString()
|
|
90
|
+
stdout += text
|
|
91
|
+
onSameLine( text )
|
|
92
|
+
} )
|
|
93
|
+
|
|
94
|
+
child.stderr.on( 'data', ( data ) => {
|
|
95
|
+
const text = data.toString()
|
|
96
|
+
stderr += text
|
|
97
|
+
onSameLine( text.replace( '\n', '' ).replace( '\r', '' ).trim() )
|
|
98
|
+
} )
|
|
99
|
+
|
|
100
|
+
child.on( 'error', done )
|
|
101
|
+
|
|
102
|
+
child.on( 'close', ( code ) => {
|
|
103
|
+
done( code ? new Error( `Command failed with exit code ${code}` ) : null )
|
|
104
|
+
} )
|
|
53
105
|
}
|
package/template/files/view.php
CHANGED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-002 / TASK-020: build waterfall must stop on composer / resource / rmSync failure.
|
|
3
|
+
* Resource build uses package.json scripts.build when present.
|
|
4
|
+
* Run with --experimental-test-module-mocks
|
|
5
|
+
*/
|
|
6
|
+
import assert from 'node:assert/strict'
|
|
7
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
8
|
+
import { tmpdir } from 'node:os'
|
|
9
|
+
import { join } from 'node:path'
|
|
10
|
+
import { mock, test, before, beforeEach } from 'node:test'
|
|
11
|
+
import fs from 'node:fs'
|
|
12
|
+
|
|
13
|
+
const execCalls = []
|
|
14
|
+
let execImpl = ( cmd, cb ) => cb( '', null, '' )
|
|
15
|
+
let potCalled = false
|
|
16
|
+
|
|
17
|
+
mock.module( import.meta.resolve( '../src/utilities/shell.js' ), {
|
|
18
|
+
exports: {
|
|
19
|
+
execCommand( cmd, cb, options = {} ) {
|
|
20
|
+
execCalls.push( { cmd, options } )
|
|
21
|
+
execImpl( cmd, cb, options )
|
|
22
|
+
},
|
|
23
|
+
runCommand() {},
|
|
24
|
+
},
|
|
25
|
+
} )
|
|
26
|
+
|
|
27
|
+
mock.module( import.meta.resolve( '../src/commands/wp-pot.js' ), {
|
|
28
|
+
exports: {
|
|
29
|
+
default( next ) {
|
|
30
|
+
potCalled = true
|
|
31
|
+
next()
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
} )
|
|
35
|
+
|
|
36
|
+
const { default: wpBuild } = await import( '../src/commands/build.js' )
|
|
37
|
+
const { pluginData } = await import( '../src/commands/release.js' )
|
|
38
|
+
|
|
39
|
+
function runBuild() {
|
|
40
|
+
return new Promise( ( resolve ) => {
|
|
41
|
+
wpBuild( ( err ) => resolve( err ) )
|
|
42
|
+
} )
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function withPackage( pkg, run ) {
|
|
46
|
+
const dir = await mkdtemp( join( tmpdir(), 'wp-advads-build-' ) )
|
|
47
|
+
await writeFile( join( dir, 'package.json' ), JSON.stringify( pkg ) )
|
|
48
|
+
const cwd = process.cwd()
|
|
49
|
+
process.chdir( dir )
|
|
50
|
+
try {
|
|
51
|
+
return await run()
|
|
52
|
+
} finally {
|
|
53
|
+
process.chdir( cwd )
|
|
54
|
+
await rm( dir, { recursive: true, force: true } )
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
before( () => {
|
|
59
|
+
mock.method( fs, 'rmSync', () => {} )
|
|
60
|
+
} )
|
|
61
|
+
|
|
62
|
+
beforeEach( () => {
|
|
63
|
+
execCalls.length = 0
|
|
64
|
+
potCalled = false
|
|
65
|
+
execImpl = ( cmd, cb ) => cb( '', null, '' )
|
|
66
|
+
fs.rmSync.mock.mockImplementation( () => {} )
|
|
67
|
+
pluginData.commitMessages = []
|
|
68
|
+
} )
|
|
69
|
+
|
|
70
|
+
test( 'composer install error stops the build before dump and pot', async () => {
|
|
71
|
+
execImpl = ( cmd, cb ) => {
|
|
72
|
+
if ( String( cmd ).includes( 'composer install' ) ) {
|
|
73
|
+
cb( '', new Error( 'install failed' ), 'install exploded' )
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
cb( '', null, '' )
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const err = await runBuild()
|
|
80
|
+
|
|
81
|
+
assert.ok( err, 'expected the build callback to receive an error' )
|
|
82
|
+
assert.equal( potCalled, false )
|
|
83
|
+
assert.equal(
|
|
84
|
+
execCalls.some( ( call ) => call.cmd === 'composer dump' ),
|
|
85
|
+
false
|
|
86
|
+
)
|
|
87
|
+
} )
|
|
88
|
+
|
|
89
|
+
test( 'npm run build error stops the build before pot', async () => {
|
|
90
|
+
await withPackage( { scripts: { build: 'mix --production' } }, async () => {
|
|
91
|
+
execImpl = ( cmd, cb ) => {
|
|
92
|
+
if ( String( cmd ).includes( 'npm run build' ) ) {
|
|
93
|
+
cb( '', new Error( 'build failed' ), 'mix exploded' )
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
cb( '', null, '' )
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const err = await runBuild()
|
|
100
|
+
|
|
101
|
+
assert.ok( err )
|
|
102
|
+
assert.equal( potCalled, false )
|
|
103
|
+
} )
|
|
104
|
+
} )
|
|
105
|
+
|
|
106
|
+
test( 'resource build runs npm run build instead of wp-scripts', async () => {
|
|
107
|
+
await withPackage( { scripts: { build: 'mix --production' } }, async () => {
|
|
108
|
+
await runBuild()
|
|
109
|
+
|
|
110
|
+
assert.equal(
|
|
111
|
+
execCalls.some( ( call ) => String( call.cmd ).includes( 'wp-scripts' ) ),
|
|
112
|
+
false
|
|
113
|
+
)
|
|
114
|
+
assert.equal(
|
|
115
|
+
execCalls.some( ( call ) => call.cmd === 'npm run build' ),
|
|
116
|
+
true
|
|
117
|
+
)
|
|
118
|
+
} )
|
|
119
|
+
} )
|
|
120
|
+
|
|
121
|
+
test( 'resource build is skipped when package.json has no build script', async () => {
|
|
122
|
+
await withPackage( { scripts: { start: 'node x' } }, async () => {
|
|
123
|
+
await runBuild()
|
|
124
|
+
|
|
125
|
+
assert.equal(
|
|
126
|
+
execCalls.some( ( call ) => String( call.cmd ).includes( 'wp-scripts' ) ),
|
|
127
|
+
false
|
|
128
|
+
)
|
|
129
|
+
assert.equal(
|
|
130
|
+
execCalls.some( ( call ) => String( call.cmd ).includes( 'npm run build' ) ),
|
|
131
|
+
false
|
|
132
|
+
)
|
|
133
|
+
assert.equal( potCalled, true )
|
|
134
|
+
} )
|
|
135
|
+
} )
|
|
136
|
+
|
|
137
|
+
test( 'composer dump receives COMPOSER_VENDOR_DIR packages', async () => {
|
|
138
|
+
await runBuild()
|
|
139
|
+
|
|
140
|
+
const dump = execCalls.find( ( call ) => call.cmd === 'composer dump' )
|
|
141
|
+
assert.ok( dump, 'expected composer dump to run' )
|
|
142
|
+
assert.equal( dump.options.env.COMPOSER_VENDOR_DIR, 'packages' )
|
|
143
|
+
} )
|
|
144
|
+
|
|
145
|
+
test( 'rmSync failure stops the build before composer', async () => {
|
|
146
|
+
fs.rmSync.mock.mockImplementation( () => {
|
|
147
|
+
throw new Error( 'EACCES' )
|
|
148
|
+
} )
|
|
149
|
+
|
|
150
|
+
const err = await runBuild()
|
|
151
|
+
|
|
152
|
+
assert.ok( err )
|
|
153
|
+
assert.equal( execCalls.length, 0 )
|
|
154
|
+
assert.equal( potCalled, false )
|
|
155
|
+
} )
|
|
156
|
+
|
|
157
|
+
test( 'standalone build does not mutate pluginData.commitMessages', async () => {
|
|
158
|
+
await runBuild()
|
|
159
|
+
assert.equal( pluginData.commitMessages.length, 0 )
|
|
160
|
+
} )
|
|
161
|
+
|
|
162
|
+
test( 'build appends messages to an optional sink', async () => {
|
|
163
|
+
const sink = []
|
|
164
|
+
await new Promise( ( resolve ) => {
|
|
165
|
+
wpBuild( () => resolve(), sink )
|
|
166
|
+
} )
|
|
167
|
+
assert.ok( sink.includes( 'update 3rd party dependencies' ) )
|
|
168
|
+
} )
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TASK-012: changelog lists git files from glotpress.destination, not pluginData.
|
|
3
|
+
* Run with --test-isolation=none --experimental-test-module-mocks
|
|
4
|
+
*/
|
|
5
|
+
import assert from 'node:assert/strict'
|
|
6
|
+
import { mkdir } from 'node:fs/promises'
|
|
7
|
+
import { mkdtemp, rm } from 'node:fs/promises'
|
|
8
|
+
import { tmpdir } from 'node:os'
|
|
9
|
+
import { join } from 'node:path'
|
|
10
|
+
import { mock, test } from 'node:test'
|
|
11
|
+
|
|
12
|
+
const execCalls = []
|
|
13
|
+
|
|
14
|
+
mock.module( import.meta.resolve( '../src/utilities/shell.js' ), {
|
|
15
|
+
exports: {
|
|
16
|
+
execCommand( cmd, cb ) {
|
|
17
|
+
execCalls.push( cmd )
|
|
18
|
+
cb( 'languages/myplugin-de_DE.po\n', null, '' )
|
|
19
|
+
},
|
|
20
|
+
runCommand() {},
|
|
21
|
+
},
|
|
22
|
+
} )
|
|
23
|
+
|
|
24
|
+
const { pluginData } = await import( '../src/commands/release.js' )
|
|
25
|
+
const { default: updateChangelog } = await import( '../src/commands/release/changelog.js' )
|
|
26
|
+
|
|
27
|
+
test( 'updateLocalesChangelog uses glotpress.destination instead of unset pluginData.glotpress', async () => {
|
|
28
|
+
const dir = await mkdtemp( join( tmpdir(), 'wp-advads-changelog-' ) )
|
|
29
|
+
const cwd = process.cwd()
|
|
30
|
+
process.chdir( dir )
|
|
31
|
+
await mkdir( join( dir, '.changelog' ) )
|
|
32
|
+
|
|
33
|
+
pluginData.semver = { preRelease: true, getNextVersionString: () => '1.2.3' }
|
|
34
|
+
pluginData.locales = { 'languages/myplugin-de_DE.po': 'German' }
|
|
35
|
+
delete pluginData.glotpress
|
|
36
|
+
execCalls.length = 0
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const err = await new Promise( ( resolve ) => {
|
|
40
|
+
updateChangelog( ( error ) => resolve( error ) )
|
|
41
|
+
} )
|
|
42
|
+
|
|
43
|
+
assert.equal( err, undefined )
|
|
44
|
+
assert.deepEqual( execCalls[ 0 ], [ 'git', 'ls-files', '--', './languages/' ] )
|
|
45
|
+
} finally {
|
|
46
|
+
process.chdir( cwd )
|
|
47
|
+
await rm( dir, { recursive: true, force: true } )
|
|
48
|
+
}
|
|
49
|
+
} )
|