wp-advads 1.0.34 → 1.0.36
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/README.md +82 -63
- package/package.json +1 -1
- package/src/app.js +7 -4
- package/src/commands/build.js +14 -5
- package/src/commands/release/changelog.js +32 -15
- package/src/commands/wp-glotpress.js +5 -5
- package/src/commands/wp-pot.js +24 -4
- package/src/utilities/shell.js +8 -44
- package/test/build.test.js +24 -1
- package/test/changelog-locales.test.js +52 -8
- package/test/pot-headers.test.js +20 -1
- package/test/shell.test.js +37 -5
- package/test/version.test.js +8 -0
package/README.md
CHANGED
|
@@ -1,139 +1,158 @@
|
|
|
1
|
-
# wp-advads
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
# wp-advads
|
|
5
2
|
|
|
6
3
|
[](https://www.npmjs.com/package/wp-advads)
|
|
7
4
|
[](LICENSE)
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
<img src="https://img.icons8.com/nolan/256/wordpress.png"/>
|
|
11
|
-
</p>
|
|
6
|
+
CLI scaffolding for an **existing** Advanced Ads WordPress plugin. It writes `wp.advads` and file stubs (`class`, `view`, `updates`, `js`, `css`). It does not generate a plugin from scratch.
|
|
12
7
|
|
|
13
|
-
|
|
8
|
+
Requires **Node.js 18+**. Created by [Shakeeb Ahmed](https://github.com/meshakeeb).
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
Created by [Shakeeb Ahmed](https://github.com/meshakeeb)
|
|
10
|
+
## Installation
|
|
17
11
|
|
|
18
|
-
## 📦 Installation
|
|
19
|
-
Use via npx:
|
|
20
12
|
```bash
|
|
21
13
|
npx wp-advads
|
|
22
14
|
```
|
|
23
15
|
|
|
24
|
-
## 🚀 Usage
|
|
25
|
-
Run the CLI using:
|
|
26
16
|
```bash
|
|
27
17
|
npx wp-advads <command> [options]
|
|
28
18
|
```
|
|
29
19
|
|
|
30
|
-
##
|
|
20
|
+
## Config
|
|
21
|
+
|
|
22
|
+
Commands (except `init`) need a `wp.advads` file. The CLI uses the nearest ancestor that already has one. `init` writes `wp.advads` in the current directory.
|
|
23
|
+
|
|
24
|
+
If the file is missing, the CLI exits and tells you to run `wp-advads init`. It does not prompt.
|
|
25
|
+
|
|
26
|
+
## Commands
|
|
31
27
|
|
|
32
28
|
#### `init`
|
|
33
|
-
|
|
29
|
+
|
|
30
|
+
Creates `wp.advads` in the current directory.
|
|
31
|
+
|
|
34
32
|
```bash
|
|
35
33
|
npx wp-advads init
|
|
36
34
|
```
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
#### `build`
|
|
37
|
+
|
|
38
|
+
Installs PHP dependencies into `packages/` (`COMPOSER_VENDOR_DIR=packages`), dumps the autoloader, runs `npm run build` when `package.json` has a `scripts.build`, then generates a `.pot`.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx wp-advads build
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Standalone `build` does not touch release git messages. During `release`, commit bullets are passed in explicitly.
|
|
39
45
|
|
|
40
46
|
#### `release`
|
|
41
|
-
|
|
47
|
+
|
|
48
|
+
Bumps the version, changelog, translations, build, and git (explicit file list, hooks run). Stops on the first failed step.
|
|
49
|
+
|
|
42
50
|
```bash
|
|
43
51
|
npx wp-advads release
|
|
44
52
|
```
|
|
45
53
|
|
|
46
|
-
|
|
47
54
|
#### `class <classname> [options]`
|
|
48
|
-
|
|
55
|
+
|
|
56
|
+
Writes a PHP class under `paths.php`. Combine `--heading` with a template flag (`-i` / `-g` / `-r` / `-s`).
|
|
57
|
+
|
|
49
58
|
```bash
|
|
50
59
|
npx wp-advads class MyClass --heading "Class Heading" --description "Description" -s
|
|
51
60
|
```
|
|
52
61
|
|
|
53
|
-
|
|
54
|
-
- `<classname>` – The name of the class.
|
|
55
|
-
|
|
56
|
-
##### Optional Argument:
|
|
62
|
+
- `<classname>` – Class name (namespaces with `\`, e.g. `Foo\Bar`)
|
|
57
63
|
- `--heading <heading>` – File heading
|
|
58
64
|
- `--description <description>` – File description
|
|
59
|
-
- `-i` – Initializer
|
|
60
|
-
- `-g` – Integration
|
|
61
|
-
- `-r` – REST
|
|
62
|
-
- `-s` – Singleton
|
|
63
|
-
|
|
65
|
+
- `-i` – Initializer
|
|
66
|
+
- `-g` – Integration
|
|
67
|
+
- `-r` – REST
|
|
68
|
+
- `-s` – Singleton (`__construct` is private)
|
|
69
|
+
- `--force` – Overwrite if the file exists
|
|
64
70
|
|
|
65
71
|
#### `view <viewname> [heading]`
|
|
66
|
-
|
|
72
|
+
|
|
73
|
+
Writes a PHP view under `paths.views`. Nested names accept `/` or `\`.
|
|
74
|
+
|
|
67
75
|
```bash
|
|
68
76
|
npx wp-advads view sidebar "Sidebar View"
|
|
77
|
+
npx wp-advads view admin/settings
|
|
69
78
|
```
|
|
70
79
|
|
|
71
|
-
|
|
72
|
-
-
|
|
80
|
+
- `<viewname>` – View file name
|
|
81
|
+
- `[heading]` – Heading comment
|
|
82
|
+
- `--force` – Overwrite if the file exists
|
|
73
83
|
|
|
74
|
-
|
|
75
|
-
- `[heading]` – A heading comment added to the view file.
|
|
84
|
+
#### `updates <version>`
|
|
76
85
|
|
|
86
|
+
Writes an upgrade file under `paths.updates`. `misc.prefix` from `wp.advads` is required (empty prefix is an error).
|
|
77
87
|
|
|
78
|
-
#### `updates <version>`
|
|
79
|
-
Creates update logic for the given plugin version.
|
|
80
88
|
```bash
|
|
81
89
|
npx wp-advads updates 1.2.3
|
|
82
90
|
```
|
|
83
91
|
|
|
84
|
-
|
|
85
|
-
-
|
|
92
|
+
- `<version>` – Target version (e.g. `1.2.3`)
|
|
93
|
+
- `--force` – Overwrite if the file exists
|
|
86
94
|
|
|
87
95
|
#### `js <filename> [options]`
|
|
88
|
-
|
|
96
|
+
|
|
97
|
+
Writes a JavaScript file under `paths.javascript`. Nested names accept `/` or `\`.
|
|
98
|
+
|
|
89
99
|
```bash
|
|
90
100
|
npx wp-advads js script -h "Main JS" -d "Handles the frontend logic"
|
|
91
101
|
```
|
|
92
102
|
|
|
93
|
-
|
|
94
|
-
-
|
|
103
|
+
- `<filename>` – Name without extension
|
|
104
|
+
- `-h <heading>` – Heading comment
|
|
105
|
+
- `-d <description>` – Description comment
|
|
106
|
+
- `--force` – Overwrite if the file exists
|
|
95
107
|
|
|
96
|
-
|
|
97
|
-
- `-h <heading>` – Heading comment for the file.
|
|
98
|
-
- `-d <description>` – Description comment for the file.
|
|
108
|
+
#### `css <filename> [options]`
|
|
99
109
|
|
|
110
|
+
Writes a CSS file under `paths.css`. Nested names accept `/` or `\`.
|
|
100
111
|
|
|
101
|
-
#### `css <filename> [options]`
|
|
102
|
-
Creates a new CSS file.
|
|
103
112
|
```bash
|
|
104
113
|
npx wp-advads css styles -h "Main CSS" -d "Plugin styles"
|
|
105
114
|
```
|
|
106
115
|
|
|
107
|
-
|
|
108
|
-
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
- `-h <heading>` – Heading comment for the file.
|
|
112
|
-
- `-d <description>` – Description comment for the file.
|
|
116
|
+
- `<filename>` – Name without extension
|
|
117
|
+
- `-h <heading>` – Heading comment
|
|
118
|
+
- `-d <description>` – Description comment
|
|
119
|
+
- `--force` – Overwrite if the file exists
|
|
113
120
|
|
|
114
121
|
#### `pot`
|
|
115
|
-
|
|
122
|
+
|
|
123
|
+
Creates a `.pot` translation template (`wp i18n make-pot`).
|
|
124
|
+
|
|
116
125
|
```bash
|
|
117
126
|
npx wp-advads pot
|
|
118
127
|
```
|
|
119
128
|
|
|
129
|
+
POT headers (`Last-Translator`, `Language-Team`) use `author.name` and `author.email` from `wp.advads`.
|
|
130
|
+
|
|
120
131
|
#### `translations`
|
|
121
|
-
|
|
132
|
+
|
|
133
|
+
Downloads `.po` / `.mo` files from GlotPress into `glotpress.destination` (default `./languages/`). Locale ids must be `[a-zA-Z0-9_]+`; non-OK HTTP responses are skipped.
|
|
134
|
+
|
|
122
135
|
```bash
|
|
123
136
|
npx wp-advads translations
|
|
124
137
|
```
|
|
125
138
|
|
|
126
|
-
|
|
127
|
-
|
|
139
|
+
## Notes
|
|
140
|
+
|
|
141
|
+
`class`, `view`, `updates`, `js`, and `css` will not overwrite an existing file unless you pass `--force`. Writes cannot escape the project root.
|
|
142
|
+
|
|
143
|
+
## Tests
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npm test
|
|
147
|
+
```
|
|
128
148
|
|
|
129
|
-
|
|
149
|
+
That runs `node --experimental-test-module-mocks --test`. Node 18+ is required (`engines.node`).
|
|
130
150
|
|
|
131
|
-
|
|
151
|
+
## Author
|
|
132
152
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
GitHub: [@meshakeeb](http://github.com/meshakeeb)
|
|
153
|
+
Shakeeb Ahmed
|
|
154
|
+
GitHub: [@meshakeeb](https://github.com/meshakeeb)
|
|
136
155
|
|
|
137
|
-
|
|
156
|
+
## Changelog
|
|
138
157
|
|
|
139
|
-
[
|
|
158
|
+
[CHANGELOG.md](./CHANGELOG.md)
|
package/package.json
CHANGED
package/src/app.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import chalk from 'chalk'
|
|
7
7
|
import { program } from 'commander'
|
|
8
|
-
import { readFileSync } from 'node:fs'
|
|
8
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
9
9
|
import { dirname, join } from 'node:path'
|
|
10
10
|
import { fileURLToPath } from 'node:url'
|
|
11
11
|
|
|
@@ -15,9 +15,11 @@ import { fileURLToPath } from 'node:url'
|
|
|
15
15
|
import * as commands from './commands/index.js'
|
|
16
16
|
import { configFileExists, onNewLine } from './utilities/index.js'
|
|
17
17
|
|
|
18
|
+
const pkgRoot = join( dirname( fileURLToPath( import.meta.url ) ), '..' )
|
|
18
19
|
const { version } = JSON.parse(
|
|
19
|
-
readFileSync( join(
|
|
20
|
+
readFileSync( join( pkgRoot, 'package.json' ), 'utf8' )
|
|
20
21
|
)
|
|
22
|
+
const fromLocal = existsSync( join( pkgRoot, '.git' ) )
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
25
|
* App
|
|
@@ -27,8 +29,9 @@ const app = async () => {
|
|
|
27
29
|
[
|
|
28
30
|
chalk.hex('#FADC00').inverse.bold('Advanced Ads WordPress Plugin Toolkit'),
|
|
29
31
|
chalk.white( 'v' + version ),
|
|
30
|
-
chalk.dim( 'by Shakeeb Ahmed' )
|
|
31
|
-
|
|
32
|
+
chalk.dim( 'by Shakeeb Ahmed' ),
|
|
33
|
+
fromLocal ? chalk.cyan( 'from local' ) : '',
|
|
34
|
+
].filter( Boolean ).join(" ")
|
|
32
35
|
);
|
|
33
36
|
|
|
34
37
|
program.configureHelp({
|
package/src/commands/build.js
CHANGED
|
@@ -27,8 +27,11 @@ export default function wpBuild(nextProcess = null, commitMessages = null) {
|
|
|
27
27
|
.catch( ( err ) => {
|
|
28
28
|
console.log('');
|
|
29
29
|
console.log( `${logSymbols.error} ${chalk.bold.red(`We failed somewhere! Work hard mate...`)}` )
|
|
30
|
+
if ( err && err !== true ) {
|
|
31
|
+
console.log( err instanceof Error ? ( err.stack || err.message ) : String( err ) )
|
|
32
|
+
}
|
|
30
33
|
if (typeof nextProcess === 'function') {
|
|
31
|
-
nextProcess(true);
|
|
34
|
+
nextProcess(err || true);
|
|
32
35
|
}
|
|
33
36
|
} )
|
|
34
37
|
}
|
|
@@ -52,11 +55,13 @@ function run( commitMessages ) {
|
|
|
52
55
|
|
|
53
56
|
await runExec( 'composer install -o -a -n --no-dev --no-scripts', composerEnv )
|
|
54
57
|
onSameLine(`${logSymbols.success} Installed PHP dependencies`);
|
|
55
|
-
process.
|
|
58
|
+
process.stdout.write('Composer dumping...')
|
|
56
59
|
|
|
57
60
|
await runExec( 'composer dump', composerEnv )
|
|
58
61
|
onSameLine(`${logSymbols.success} Composer updated`)
|
|
59
|
-
|
|
62
|
+
if ( Array.isArray( commitMessages ) ) {
|
|
63
|
+
commitMessages.push( 'update 3rd party dependencies' )
|
|
64
|
+
}
|
|
60
65
|
|
|
61
66
|
process.stdout.write('Building plugin resources...')
|
|
62
67
|
let buildScript
|
|
@@ -71,7 +76,9 @@ function run( commitMessages ) {
|
|
|
71
76
|
} else {
|
|
72
77
|
await runExec( 'npm run build' )
|
|
73
78
|
onSameLine(`${logSymbols.success} Built plugin resources`);
|
|
74
|
-
|
|
79
|
+
if ( Array.isArray( commitMessages ) ) {
|
|
80
|
+
commitMessages.push( 'Build plugin resources' )
|
|
81
|
+
}
|
|
75
82
|
}
|
|
76
83
|
|
|
77
84
|
console.log('')
|
|
@@ -85,7 +92,9 @@ function runExec( cmd, options ) {
|
|
|
85
92
|
return new Promise( ( resolve, reject ) => {
|
|
86
93
|
execCommand( cmd, ( stdout, error, stderr ) => {
|
|
87
94
|
if ( error ) {
|
|
88
|
-
|
|
95
|
+
const detail = [ stderr && stderr.trim(), error.message ].filter( Boolean ).join( '\n' )
|
|
96
|
+
onSameLine(`${logSymbols.error} ${detail}`)
|
|
97
|
+
error.message = detail
|
|
89
98
|
reject( error )
|
|
90
99
|
return
|
|
91
100
|
}
|
|
@@ -22,34 +22,46 @@ const LOCALES_CHANGELOG = {
|
|
|
22
22
|
*
|
|
23
23
|
* @param {Array} list List of languages downloaded
|
|
24
24
|
*
|
|
25
|
-
* @returns {
|
|
25
|
+
* @returns {string}
|
|
26
26
|
*/
|
|
27
27
|
function naturalLanguageJoin(list) {
|
|
28
|
-
|
|
28
|
+
if ( list.length === 0 ) {
|
|
29
|
+
return ''
|
|
30
|
+
}
|
|
29
31
|
|
|
30
|
-
if ( list.length
|
|
31
|
-
return
|
|
32
|
+
if ( list.length === 1 ) {
|
|
33
|
+
return list[0]
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
return
|
|
36
|
+
return `${list.slice( 0, -1 ).join(', ')} and ${list[ list.length - 1 ]}`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeGitPath( file ) {
|
|
40
|
+
return String( file || '' )
|
|
41
|
+
.replace( /\\/g, '/' )
|
|
42
|
+
.replace( /^\.\//, '' )
|
|
43
|
+
.replace( /\/+/g, '/' )
|
|
35
44
|
}
|
|
36
45
|
|
|
37
46
|
/**
|
|
38
47
|
* Loop through downloaded locales and categorize them as added or updated
|
|
39
48
|
*
|
|
40
|
-
* @param {
|
|
49
|
+
* @param {Object} locales Locales downloaded from GlotPress
|
|
41
50
|
* @param {Array} files Files currently in branch
|
|
42
51
|
*
|
|
43
|
-
* @returns {
|
|
52
|
+
* @returns {Object}
|
|
44
53
|
*/
|
|
45
54
|
function loopLocales( locales, files ) {
|
|
46
55
|
const changes = { updated: [], added: [] }
|
|
47
|
-
const
|
|
56
|
+
const tracked = new Set( files.map( normalizeGitPath ).filter( Boolean ) )
|
|
48
57
|
|
|
49
|
-
|
|
50
|
-
const action =
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
for ( const [ file, name ] of Object.entries( locales || {} ) ) {
|
|
59
|
+
const action = tracked.has( normalizeGitPath( file ) ) ? 'updated' : 'added'
|
|
60
|
+
const label = name || file.split( '/' ).pop()
|
|
61
|
+
if ( label ) {
|
|
62
|
+
changes[ action ].push( label )
|
|
63
|
+
}
|
|
64
|
+
}
|
|
53
65
|
|
|
54
66
|
changes.added = [... new Set(changes.added)].sort()
|
|
55
67
|
changes.updated = [... new Set(changes.updated)].sort()
|
|
@@ -70,7 +82,12 @@ function writeLocalesChangelog(type, changes) {
|
|
|
70
82
|
return
|
|
71
83
|
}
|
|
72
84
|
|
|
73
|
-
const message =
|
|
85
|
+
const message = naturalLanguageJoin(changes)
|
|
86
|
+
if ( ! message ) {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const line = LOCALES_CHANGELOG[type].message.replace('%s', message)
|
|
74
91
|
|
|
75
92
|
if (!fs.existsSync('./.changelog')) {
|
|
76
93
|
fs.mkdirSync('./.changelog');
|
|
@@ -78,7 +95,7 @@ function writeLocalesChangelog(type, changes) {
|
|
|
78
95
|
|
|
79
96
|
fs.writeFileSync(
|
|
80
97
|
`./.changelog/${LOCALES_CHANGELOG[type].file}`,
|
|
81
|
-
|
|
98
|
+
line
|
|
82
99
|
)
|
|
83
100
|
}
|
|
84
101
|
|
|
@@ -91,7 +108,7 @@ function updateLocalesChangelog(next) {
|
|
|
91
108
|
write('Generating translations changelog...')
|
|
92
109
|
|
|
93
110
|
execCommand( [ 'git', 'ls-files', '--', destination ], function(result) {
|
|
94
|
-
const files = result.split('\n').
|
|
111
|
+
const files = String( result || '' ).split('\n').map( ( line ) => line.trim() ).filter( Boolean )
|
|
95
112
|
const changes = loopLocales(pluginData.locales, files)
|
|
96
113
|
|
|
97
114
|
writeLocalesChangelog('added', changes.added)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
|
-
import { resolve } from 'path';
|
|
6
|
+
import { join, relative, resolve } from 'path';
|
|
7
7
|
import { heading, msgError, getSetting, write, msgSuccessOnSameLine, onSameLine, msgErrorOnSameLine } from "../utilities/index.js";
|
|
8
8
|
|
|
9
9
|
const BASE_URL = 'https://translate.wpadvancedads.com/api/projects';
|
|
@@ -75,15 +75,15 @@ async function downloadAll( ctx ) {
|
|
|
75
75
|
console.log( '' );
|
|
76
76
|
console.log(
|
|
77
77
|
chalk.yellow.bold( 'Updating Language:' ) +
|
|
78
|
-
` ${chalk.italic( set.name )}`
|
|
78
|
+
` ${chalk.italic( set.name || locale )}`
|
|
79
79
|
);
|
|
80
80
|
|
|
81
81
|
for ( const format of [ 'mo', 'po' ] ) {
|
|
82
82
|
const target = await downloadFile( ctx, locale, set, format );
|
|
83
83
|
if ( target ) {
|
|
84
|
-
const file = target.replace(
|
|
84
|
+
const file = relative( process.cwd(), resolve( target ) ).replace( /\\/g, '/' );
|
|
85
85
|
files.push( file );
|
|
86
|
-
names[ file ] = set.name;
|
|
86
|
+
names[ file ] = set.name || locale;
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -142,7 +142,7 @@ async function downloadFile(ctx, locale, data, format) {
|
|
|
142
142
|
chalk.bold(`Downloading: ${ctx.potPrefix}-${locale}.${format}`)
|
|
143
143
|
);
|
|
144
144
|
|
|
145
|
-
const target =
|
|
145
|
+
const target = join( ctx.destination, `${ctx.potPrefix}-${locale}.${format}` );
|
|
146
146
|
const endpoint = `${BASE_URL}/${ctx.project}/${data.locale}/${data.slug}/export-translations?format=${format}`;
|
|
147
147
|
|
|
148
148
|
const response = await fetch(endpoint, {
|
package/src/commands/wp-pot.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Internal Dependencies
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { write, msgSuccessOnSameLine, msgErrorOnSameLine, runCommand, getSetting } from "../utilities/index.js"
|
|
5
5
|
|
|
6
6
|
export default (next = null) => {
|
|
7
|
-
|
|
7
|
+
write('Generating POT file...')
|
|
8
8
|
|
|
9
9
|
const settings = getSetting()
|
|
10
10
|
|
|
@@ -38,15 +38,35 @@ export default (next = null) => {
|
|
|
38
38
|
`--domain=${domain}`,
|
|
39
39
|
`--exclude=${exclude.join(',')}`,
|
|
40
40
|
`--headers=${JSON.stringify(headers)}`,
|
|
41
|
-
], ( error ) => {
|
|
41
|
+
], ( error, stdout, stderr ) => {
|
|
42
42
|
if ( error ) {
|
|
43
|
+
msgErrorOnSameLine(error.message)
|
|
43
44
|
if ( next ) {
|
|
44
45
|
next( error )
|
|
45
46
|
}
|
|
46
47
|
return
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
msgSuccessOnSameLine('POT file generated successfully');
|
|
51
|
+
printPotWarnings( stderr, stdout )
|
|
50
52
|
if (next) next();
|
|
51
53
|
} );
|
|
52
54
|
}
|
|
55
|
+
|
|
56
|
+
function printPotWarnings( stderr, stdout ) {
|
|
57
|
+
const warnings = [ stderr, stdout ]
|
|
58
|
+
.filter( Boolean )
|
|
59
|
+
.join( '\n' )
|
|
60
|
+
.split( /\r?\n/ )
|
|
61
|
+
.map( ( line ) => line.trim() )
|
|
62
|
+
.filter( ( line ) => /warning:/i.test( line ) )
|
|
63
|
+
|
|
64
|
+
if ( ! warnings.length ) {
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
console.log( '' )
|
|
69
|
+
for ( const line of warnings ) {
|
|
70
|
+
console.log( line )
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/utilities/shell.js
CHANGED
|
@@ -3,11 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { spawn } from 'node:child_process'
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* Internal Dependencies
|
|
8
|
-
*/
|
|
9
|
-
import { onSameLine } from './formatting.js'
|
|
10
|
-
|
|
11
6
|
/**
|
|
12
7
|
* Run a command in the terminal.
|
|
13
8
|
*
|
|
@@ -18,39 +13,12 @@ import { onSameLine } from './formatting.js'
|
|
|
18
13
|
* @returns {void}
|
|
19
14
|
*/
|
|
20
15
|
export function runCommand( command, args, callback ) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const done = ( err ) => {
|
|
28
|
-
if ( finished ) {
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
finished = true
|
|
33
|
-
callback( err )
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
commandSpawn.stdout.on( 'data', (data) => {
|
|
37
|
-
onSameLine(data.toString())
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
commandSpawn.stderr.on( 'data', (data) => {
|
|
41
|
-
onSameLine(data.toString().replace('\n','').replace('\r','').trim())
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
commandSpawn.on( 'error', done )
|
|
45
|
-
|
|
46
|
-
commandSpawn.on( 'close', (code) => {
|
|
47
|
-
if ( code !== 0 ) {
|
|
48
|
-
done( new Error( `Command failed with exit code ${code}` ) )
|
|
49
|
-
return
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
done()
|
|
53
|
-
})
|
|
16
|
+
execCommand( [ command, ...args ], ( stdout, error, stderr ) => {
|
|
17
|
+
if ( error ) {
|
|
18
|
+
error.message = [ stderr && stderr.trim(), error.message ].filter( Boolean ).join( '\n' )
|
|
19
|
+
}
|
|
20
|
+
callback( error, stdout, stderr )
|
|
21
|
+
} )
|
|
54
22
|
}
|
|
55
23
|
|
|
56
24
|
/**
|
|
@@ -86,15 +54,11 @@ export function execCommand( command, callback, options = {} ) {
|
|
|
86
54
|
}
|
|
87
55
|
|
|
88
56
|
child.stdout.on( 'data', ( data ) => {
|
|
89
|
-
|
|
90
|
-
stdout += text
|
|
91
|
-
onSameLine( text )
|
|
57
|
+
stdout += data.toString()
|
|
92
58
|
} )
|
|
93
59
|
|
|
94
60
|
child.stderr.on( 'data', ( data ) => {
|
|
95
|
-
|
|
96
|
-
stderr += text
|
|
97
|
-
onSameLine( text.replace( '\n', '' ).replace( '\r', '' ).trim() )
|
|
61
|
+
stderr += data.toString()
|
|
98
62
|
} )
|
|
99
63
|
|
|
100
64
|
child.on( 'error', done )
|
package/test/build.test.js
CHANGED
|
@@ -13,6 +13,7 @@ import fs from 'node:fs'
|
|
|
13
13
|
const execCalls = []
|
|
14
14
|
let execImpl = ( cmd, cb ) => cb( '', null, '' )
|
|
15
15
|
let potCalled = false
|
|
16
|
+
let potImpl = ( next ) => next()
|
|
16
17
|
|
|
17
18
|
mock.module( import.meta.resolve( '../src/utilities/shell.js' ), {
|
|
18
19
|
exports: {
|
|
@@ -28,7 +29,7 @@ mock.module( import.meta.resolve( '../src/commands/wp-pot.js' ), {
|
|
|
28
29
|
exports: {
|
|
29
30
|
default( next ) {
|
|
30
31
|
potCalled = true
|
|
31
|
-
next
|
|
32
|
+
potImpl( next )
|
|
32
33
|
},
|
|
33
34
|
},
|
|
34
35
|
} )
|
|
@@ -63,6 +64,7 @@ beforeEach( () => {
|
|
|
63
64
|
execCalls.length = 0
|
|
64
65
|
potCalled = false
|
|
65
66
|
execImpl = ( cmd, cb ) => cb( '', null, '' )
|
|
67
|
+
potImpl = ( next ) => next()
|
|
66
68
|
fs.rmSync.mock.mockImplementation( () => {} )
|
|
67
69
|
pluginData.commitMessages = []
|
|
68
70
|
} )
|
|
@@ -166,3 +168,24 @@ test( 'build appends messages to an optional sink', async () => {
|
|
|
166
168
|
} )
|
|
167
169
|
assert.ok( sink.includes( 'update 3rd party dependencies' ) )
|
|
168
170
|
} )
|
|
171
|
+
|
|
172
|
+
test( 'build ignores a non-array commitMessages sink', async () => {
|
|
173
|
+
await new Promise( ( resolve, reject ) => {
|
|
174
|
+
wpBuild( ( err ) => err ? reject( err ) : resolve(), { name: () => 'build' } )
|
|
175
|
+
} )
|
|
176
|
+
} )
|
|
177
|
+
|
|
178
|
+
test( 'build failure prints the rejected error', async () => {
|
|
179
|
+
const lines = []
|
|
180
|
+
mock.method( console, 'log', ( ...args ) => {
|
|
181
|
+
lines.push( args.map( String ).join( ' ' ) )
|
|
182
|
+
} )
|
|
183
|
+
potImpl = ( next ) => next( new Error( 'make-pot failed: wp: command not found' ) )
|
|
184
|
+
|
|
185
|
+
await runBuild()
|
|
186
|
+
|
|
187
|
+
assert.ok(
|
|
188
|
+
lines.some( ( line ) => line.includes( 'make-pot failed: wp: command not found' ) ),
|
|
189
|
+
`expected the pot error in logs, got:\n${lines.join( '\n' )}`
|
|
190
|
+
)
|
|
191
|
+
} )
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Run with --test-isolation=none --experimental-test-module-mocks
|
|
4
4
|
*/
|
|
5
5
|
import assert from 'node:assert/strict'
|
|
6
|
+
import { readFileSync } from 'node:fs'
|
|
6
7
|
import { mkdir } from 'node:fs/promises'
|
|
7
8
|
import { mkdtemp, rm } from 'node:fs/promises'
|
|
8
9
|
import { tmpdir } from 'node:os'
|
|
@@ -10,12 +11,13 @@ import { join } from 'node:path'
|
|
|
10
11
|
import { mock, test } from 'node:test'
|
|
11
12
|
|
|
12
13
|
const execCalls = []
|
|
14
|
+
let gitFiles = 'languages/myplugin-de_DE.po\n'
|
|
13
15
|
|
|
14
16
|
mock.module( import.meta.resolve( '../src/utilities/shell.js' ), {
|
|
15
17
|
exports: {
|
|
16
18
|
execCommand( cmd, cb ) {
|
|
17
19
|
execCalls.push( cmd )
|
|
18
|
-
cb(
|
|
20
|
+
cb( gitFiles, null, '' )
|
|
19
21
|
},
|
|
20
22
|
runCommand() {},
|
|
21
23
|
},
|
|
@@ -24,26 +26,68 @@ mock.module( import.meta.resolve( '../src/utilities/shell.js' ), {
|
|
|
24
26
|
const { pluginData } = await import( '../src/commands/release.js' )
|
|
25
27
|
const { default: updateChangelog } = await import( '../src/commands/release/changelog.js' )
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
async function withChangelog( run ) {
|
|
28
30
|
const dir = await mkdtemp( join( tmpdir(), 'wp-advads-changelog-' ) )
|
|
29
31
|
const cwd = process.cwd()
|
|
30
32
|
process.chdir( dir )
|
|
31
33
|
await mkdir( join( dir, '.changelog' ) )
|
|
32
|
-
|
|
33
34
|
pluginData.semver = { preRelease: true, getNextVersionString: () => '1.2.3' }
|
|
34
|
-
pluginData.locales = { 'languages/myplugin-de_DE.po': 'German' }
|
|
35
35
|
delete pluginData.glotpress
|
|
36
36
|
execCalls.length = 0
|
|
37
37
|
|
|
38
38
|
try {
|
|
39
|
+
return await run( dir )
|
|
40
|
+
} finally {
|
|
41
|
+
process.chdir( cwd )
|
|
42
|
+
await rm( dir, { recursive: true, force: true } )
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function changelog( dir, file ) {
|
|
47
|
+
return readFileSync( join( dir, '.changelog', file ), 'utf8' )
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
test( 'updateLocalesChangelog lists updated languages from glotpress.destination', async () => {
|
|
51
|
+
gitFiles = [
|
|
52
|
+
'languages/myplugin-de_DE.po',
|
|
53
|
+
'languages/myplugin-de_DE.mo',
|
|
54
|
+
'languages/myplugin-fr_FR.po',
|
|
55
|
+
].join( '\n' ) + '\n'
|
|
56
|
+
pluginData.locales = {
|
|
57
|
+
'languages/myplugin-de_DE.po': 'German',
|
|
58
|
+
'languages/myplugin-de_DE.mo': 'German',
|
|
59
|
+
'languages/myplugin-fr_FR.po': 'French (France)',
|
|
60
|
+
'languages/myplugin-it_IT.po': 'Italian',
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
await withChangelog( async ( dir ) => {
|
|
39
64
|
const err = await new Promise( ( resolve ) => {
|
|
40
65
|
updateChangelog( ( error ) => resolve( error ) )
|
|
41
66
|
} )
|
|
42
67
|
|
|
43
68
|
assert.equal( err, undefined )
|
|
44
69
|
assert.deepEqual( execCalls[ 0 ], [ 'git', 'ls-files', '--', './languages/' ] )
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
70
|
+
assert.match( changelog( dir, 'improvement-102' ), /German/ )
|
|
71
|
+
assert.match( changelog( dir, 'improvement-102' ), /French \(France\)/ )
|
|
72
|
+
assert.doesNotMatch( changelog( dir, 'improvement-102' ), /undefined/ )
|
|
73
|
+
assert.match( changelog( dir, 'improvement-101' ), /Italian/ )
|
|
74
|
+
assert.doesNotMatch( changelog( dir, 'improvement-101' ), /undefined/ )
|
|
75
|
+
} )
|
|
76
|
+
|
|
77
|
+
gitFiles = 'languages/myplugin-de_DE.po\n'
|
|
78
|
+
pluginData.locales = { 'languages//myplugin-de_DE.po': 'German' }
|
|
79
|
+
|
|
80
|
+
await withChangelog( async ( dir ) => {
|
|
81
|
+
await new Promise( ( resolve ) => updateChangelog( resolve ) )
|
|
82
|
+
assert.match( changelog( dir, 'improvement-102' ), /update German translations/ )
|
|
83
|
+
} )
|
|
84
|
+
|
|
85
|
+
pluginData.locales = { 'languages/myplugin-de_DE.po': undefined }
|
|
86
|
+
|
|
87
|
+
await withChangelog( async ( dir ) => {
|
|
88
|
+
await new Promise( ( resolve ) => updateChangelog( resolve ) )
|
|
89
|
+
const updated = changelog( dir, 'improvement-102' )
|
|
90
|
+
assert.doesNotMatch( updated, /undefined/ )
|
|
91
|
+
assert.match( updated, /myplugin-de_DE\.po/ )
|
|
92
|
+
} )
|
|
49
93
|
} )
|
package/test/pot-headers.test.js
CHANGED
|
@@ -9,13 +9,14 @@ import { join } from 'node:path'
|
|
|
9
9
|
import { mock, test } from 'node:test'
|
|
10
10
|
|
|
11
11
|
const potArgs = []
|
|
12
|
+
let potStderr = ''
|
|
12
13
|
|
|
13
14
|
mock.module( import.meta.resolve( '../src/utilities/shell.js' ), {
|
|
14
15
|
exports: {
|
|
15
16
|
execCommand() {},
|
|
16
17
|
runCommand( cmd, args, cb ) {
|
|
17
18
|
potArgs.push( { cmd, args } )
|
|
18
|
-
cb()
|
|
19
|
+
cb( null, '', potStderr )
|
|
19
20
|
},
|
|
20
21
|
},
|
|
21
22
|
} )
|
|
@@ -42,6 +43,24 @@ test( 'pot Last-Translator and Language-Team use author from config', async () =
|
|
|
42
43
|
assert.equal( headers['Last-Translator'], 'Jane Doe <jane@example.com>' )
|
|
43
44
|
assert.equal( headers['Language-Team'], 'Jane Doe <jane@example.com>' )
|
|
44
45
|
assert.doesNotMatch( headersArg, /Shakeeb Ahmed/ )
|
|
46
|
+
|
|
47
|
+
potStderr = 'Warning: The string "Hello" is missing a translator comment.'
|
|
48
|
+
const lines = []
|
|
49
|
+
const originalLog = console.log
|
|
50
|
+
console.log = ( msg ) => {
|
|
51
|
+
lines.push( String( msg ) )
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
await new Promise( ( resolve ) => wpPot( resolve ) )
|
|
55
|
+
} finally {
|
|
56
|
+
console.log = originalLog
|
|
57
|
+
potStderr = ''
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
assert.ok(
|
|
61
|
+
lines.some( ( line ) => line.includes( 'Warning: The string "Hello" is missing a translator comment.' ) ),
|
|
62
|
+
`expected make-pot warnings in logs, got:\n${lines.join( '\n' )}`
|
|
63
|
+
)
|
|
45
64
|
} finally {
|
|
46
65
|
process.chdir( cwd )
|
|
47
66
|
await rm( dir, { recursive: true, force: true } )
|
package/test/shell.test.js
CHANGED
|
@@ -30,7 +30,7 @@ test( 'runCommand calls back with an error on non-zero exit', async () => {
|
|
|
30
30
|
assert.ok( error, 'expected an error on exit 1' )
|
|
31
31
|
} )
|
|
32
32
|
|
|
33
|
-
test( '
|
|
33
|
+
test( 'runCommand does not print stdout or stderr when the command succeeds', async () => {
|
|
34
34
|
const lines = []
|
|
35
35
|
const originalLog = console.log
|
|
36
36
|
console.log = ( msg ) => {
|
|
@@ -38,14 +38,46 @@ test( 'execCommand prints stderr when the command fails', async () => {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
try {
|
|
41
|
-
await
|
|
41
|
+
const error = await runOnce( 'node', [
|
|
42
|
+
'-e',
|
|
43
|
+
'process.stdout.write("make-pot progress"); process.stderr.write("scanning files")',
|
|
44
|
+
] )
|
|
45
|
+
assert.ok( ! error )
|
|
46
|
+
} finally {
|
|
47
|
+
console.log = originalLog
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
assert.equal(
|
|
51
|
+
lines.some( ( line ) => line.includes( 'make-pot progress' ) || line.includes( 'scanning files' ) ),
|
|
52
|
+
false
|
|
53
|
+
)
|
|
54
|
+
} )
|
|
55
|
+
|
|
56
|
+
test( 'execCommand returns stderr when the command fails', async () => {
|
|
57
|
+
const { error, stderr } = await execOnce( 'node -e "process.stderr.write(\'boom-fail\'); process.exit(1)"' )
|
|
58
|
+
assert.ok( error )
|
|
59
|
+
assert.match( stderr, /boom-fail/ )
|
|
60
|
+
} )
|
|
61
|
+
|
|
62
|
+
test( 'execCommand does not print stdout or stderr when the command succeeds', async () => {
|
|
63
|
+
const lines = []
|
|
64
|
+
const originalLog = console.log
|
|
65
|
+
console.log = ( msg ) => {
|
|
66
|
+
lines.push( String( msg ) )
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const { error } = await execOnce(
|
|
71
|
+
'node -e "process.stdout.write(\'Installing dependencies from lock file\'); process.stderr.write(\'Extracting archive\')"'
|
|
72
|
+
)
|
|
73
|
+
assert.equal( error, null )
|
|
42
74
|
} finally {
|
|
43
75
|
console.log = originalLog
|
|
44
76
|
}
|
|
45
77
|
|
|
46
|
-
assert.
|
|
47
|
-
lines.some( ( line ) => line.includes( '
|
|
48
|
-
|
|
78
|
+
assert.equal(
|
|
79
|
+
lines.some( ( line ) => line.includes( 'Installing dependencies from lock file' ) || line.includes( 'Extracting archive' ) ),
|
|
80
|
+
false
|
|
49
81
|
)
|
|
50
82
|
} )
|
|
51
83
|
|
package/test/version.test.js
CHANGED
|
@@ -20,3 +20,11 @@ test( 'wp-advads --version matches package.json and is not hardcoded 2.0.0', asy
|
|
|
20
20
|
assert.match( stdout, new RegExp( `\\b${version}\\b` ) )
|
|
21
21
|
assert.doesNotMatch( stdout, /v2\.0\.0/ )
|
|
22
22
|
} )
|
|
23
|
+
|
|
24
|
+
test( 'banner says from local when the CLI is a git checkout', async () => {
|
|
25
|
+
const { stdout } = await execFileAsync( process.execPath, [ 'src/app.js', '--version' ], {
|
|
26
|
+
cwd: root,
|
|
27
|
+
} )
|
|
28
|
+
|
|
29
|
+
assert.match( stdout, /from local/ )
|
|
30
|
+
} )
|