@cloudcatch/wp-esbuild 1.1.0
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/LICENSE +21 -0
- package/README.md +516 -0
- package/bin/wp-esbuild.mjs +36 -0
- package/lib/build-blocks-manifest.mjs +63 -0
- package/lib/build.mjs +111 -0
- package/lib/config.mjs +96 -0
- package/lib/define-config.mjs +51 -0
- package/lib/handlers/blocks.mjs +159 -0
- package/lib/handlers/copy.mjs +59 -0
- package/lib/handlers/index.mjs +44 -0
- package/lib/handlers/script.mjs +141 -0
- package/lib/handlers/scss.mjs +113 -0
- package/lib/load-config.mjs +103 -0
- package/lib/merge-esbuild-options.mjs +51 -0
- package/lib/normalize-config.mjs +267 -0
- package/lib/php-export.mjs +62 -0
- package/lib/postcss.mjs +82 -0
- package/lib/resolve-scss-outfile.mjs +75 -0
- package/lib/utils.mjs +29 -0
- package/lib/wordpress-externals-plugin.mjs +320 -0
- package/lib/write-asset-php.mjs +35 -0
- package/package.json +51 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCSS/CSS handler.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import esbuild from 'esbuild';
|
|
6
|
+
import { sassPlugin } from 'esbuild-sass-plugin';
|
|
7
|
+
import glob from 'fast-glob';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { mkdir, readFile, writeFile } from 'fs/promises';
|
|
10
|
+
import { fileExists } from '../utils.mjs';
|
|
11
|
+
import { generateRtlCss, loadPostcssPlugins, postprocessCss } from '../postcss.mjs';
|
|
12
|
+
import { resolveScssOutfile } from '../resolve-scss-outfile.mjs';
|
|
13
|
+
import { writeAssetPhp } from '../write-asset-php.mjs';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {object} options
|
|
17
|
+
* @param {string} options.entry
|
|
18
|
+
* @param {string} options.outfile
|
|
19
|
+
* @param {object} options.buildContext
|
|
20
|
+
* @param {object} options.entryConfig
|
|
21
|
+
* @return {Promise<void>}
|
|
22
|
+
*/
|
|
23
|
+
export async function buildScssFile( { entry, outfile, buildContext, entryConfig } ) {
|
|
24
|
+
const { minify, sourcemap, projectRoot, postcss: postcssSetting, rtl } = buildContext;
|
|
25
|
+
const postcssPlugins = await loadPostcssPlugins( projectRoot, postcssSetting );
|
|
26
|
+
const shouldRtl = entryConfig.rtl ?? rtl;
|
|
27
|
+
|
|
28
|
+
await mkdir( path.dirname( outfile ), { recursive: true } );
|
|
29
|
+
|
|
30
|
+
const result = await esbuild.build( {
|
|
31
|
+
entryPoints: [ entry ],
|
|
32
|
+
outfile,
|
|
33
|
+
bundle: true,
|
|
34
|
+
minify,
|
|
35
|
+
sourcemap,
|
|
36
|
+
logLevel: 'silent',
|
|
37
|
+
plugins: [
|
|
38
|
+
sassPlugin( {
|
|
39
|
+
type: 'css',
|
|
40
|
+
sourceMap: sourcemap,
|
|
41
|
+
} ),
|
|
42
|
+
],
|
|
43
|
+
} );
|
|
44
|
+
|
|
45
|
+
if ( result.errors.length > 0 ) {
|
|
46
|
+
throw new Error( result.errors.map( ( e ) => e.text ).join( '\n' ) );
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const css = await readFile( outfile, 'utf8' );
|
|
50
|
+
const processed = await postprocessCss( css, postcssPlugins );
|
|
51
|
+
await writeFile( outfile, processed );
|
|
52
|
+
|
|
53
|
+
if ( shouldRtl ) {
|
|
54
|
+
const rtlCss = await generateRtlCss( processed );
|
|
55
|
+
const rtlOutfile = outfile.replace( /\.css$/, '-rtl.css' );
|
|
56
|
+
await writeFile( rtlOutfile, rtlCss );
|
|
57
|
+
|
|
58
|
+
if ( entryConfig.assetPhp ) {
|
|
59
|
+
await writeAssetPhp( rtlOutfile );
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if ( entryConfig.assetPhp ) {
|
|
64
|
+
await writeAssetPhp( outfile, {
|
|
65
|
+
dependencies: entryConfig.assetDependencies || [],
|
|
66
|
+
} );
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {string} projectRoot
|
|
72
|
+
* @param {object} entryConfig
|
|
73
|
+
* @param {object} buildContext
|
|
74
|
+
* @return {Promise<void>}
|
|
75
|
+
*/
|
|
76
|
+
export async function runScssHandler( projectRoot, entryConfig, buildContext ) {
|
|
77
|
+
const srcDir = path.join( projectRoot, entryConfig.src );
|
|
78
|
+
const outDir = path.join( projectRoot, entryConfig.out );
|
|
79
|
+
|
|
80
|
+
if ( ! ( await fileExists( srcDir ) ) ) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const globPattern = entryConfig.glob || '*.{scss,sass}';
|
|
85
|
+
const ignore = entryConfig.ignore || [ '**/_*' ];
|
|
86
|
+
|
|
87
|
+
const entries = await glob( globPattern, {
|
|
88
|
+
cwd: srcDir,
|
|
89
|
+
absolute: true,
|
|
90
|
+
ignore,
|
|
91
|
+
} );
|
|
92
|
+
|
|
93
|
+
for ( const entry of entries ) {
|
|
94
|
+
const outfile = resolveScssOutfile( {
|
|
95
|
+
srcDir,
|
|
96
|
+
outDir,
|
|
97
|
+
entry,
|
|
98
|
+
entryConfig,
|
|
99
|
+
} );
|
|
100
|
+
|
|
101
|
+
await buildScssFile( {
|
|
102
|
+
entry,
|
|
103
|
+
outfile,
|
|
104
|
+
buildContext,
|
|
105
|
+
entryConfig,
|
|
106
|
+
} );
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const scssHandler = {
|
|
111
|
+
type: 'scss',
|
|
112
|
+
run: runScssHandler,
|
|
113
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load and merge project wp-esbuild.config.mjs.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { pathToFileURL } from 'url';
|
|
7
|
+
import { defaultProjectConfig } from './config.mjs';
|
|
8
|
+
import { normalizeConfig } from './normalize-config.mjs';
|
|
9
|
+
|
|
10
|
+
const CONFIG_FILENAME = 'wp-esbuild.config.mjs';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} projectRoot
|
|
14
|
+
* @return {Promise<object>}
|
|
15
|
+
*/
|
|
16
|
+
export async function loadProjectConfig( projectRoot ) {
|
|
17
|
+
const configPath = path.join( projectRoot, CONFIG_FILENAME );
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const module = await import( pathToFileURL( configPath ).href );
|
|
21
|
+
const userConfig =
|
|
22
|
+
typeof module.default === 'function'
|
|
23
|
+
? module.default( { env: process.env } )
|
|
24
|
+
: module.default;
|
|
25
|
+
|
|
26
|
+
return normalizeConfig( mergeConfig( defaultProjectConfig, userConfig || {} ) );
|
|
27
|
+
} catch ( error ) {
|
|
28
|
+
if (
|
|
29
|
+
error &&
|
|
30
|
+
typeof error === 'object' &&
|
|
31
|
+
'code' in error &&
|
|
32
|
+
error.code === 'ERR_MODULE_NOT_FOUND'
|
|
33
|
+
) {
|
|
34
|
+
return normalizeConfig( { ...defaultProjectConfig } );
|
|
35
|
+
}
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {object} base
|
|
42
|
+
* @param {object} override
|
|
43
|
+
* @return {object}
|
|
44
|
+
*/
|
|
45
|
+
function mergeConfig( base, override ) {
|
|
46
|
+
const merged = {
|
|
47
|
+
...base,
|
|
48
|
+
...override,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
if ( override.blocks !== undefined ) {
|
|
52
|
+
merged.blocks = Array.isArray( override.blocks )
|
|
53
|
+
? override.blocks
|
|
54
|
+
: { ...base.blocks, ...override.blocks };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if ( override.js !== undefined ) {
|
|
58
|
+
merged.js = Array.isArray( override.js )
|
|
59
|
+
? override.js
|
|
60
|
+
: { ...base.js, ...override.js };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if ( override.modules !== undefined ) {
|
|
64
|
+
merged.modules = Array.isArray( override.modules )
|
|
65
|
+
? override.modules
|
|
66
|
+
: { ...base.modules, ...override.modules };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if ( override.scss !== undefined ) {
|
|
70
|
+
merged.scss = Array.isArray( override.scss )
|
|
71
|
+
? override.scss
|
|
72
|
+
: { ...base.scss, ...override.scss };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if ( override.copy !== undefined ) {
|
|
76
|
+
merged.copy = Array.isArray( override.copy ) ? override.copy : override.copy;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if ( override.blocksManifest ) {
|
|
80
|
+
merged.blocksManifest = { ...base.blocksManifest, ...override.blocksManifest };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if ( override.wordpressExternals ) {
|
|
84
|
+
merged.wordpressExternals = {
|
|
85
|
+
...base.wordpressExternals,
|
|
86
|
+
...override.wordpressExternals,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if ( override.esbuild ) {
|
|
91
|
+
merged.esbuild = { ...base.esbuild, ...override.esbuild };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if ( override.plugins ) {
|
|
95
|
+
merged.plugins = override.plugins;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if ( override.entries ) {
|
|
99
|
+
merged.entries = override.entries;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return merged;
|
|
103
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge default, global, and per-entry esbuild options.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { getDefaultEsbuildOptions } from './config.mjs';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {object} params
|
|
10
|
+
* @param {boolean} params.minify
|
|
11
|
+
* @param {boolean} params.sourcemap
|
|
12
|
+
* @param {object} [params.globalEsbuild]
|
|
13
|
+
* @param {object} [params.entryEsbuild]
|
|
14
|
+
* @param {string} [params.projectRoot]
|
|
15
|
+
* @return {import('esbuild').BuildOptions}
|
|
16
|
+
*/
|
|
17
|
+
export function mergeEsbuildOptions( {
|
|
18
|
+
minify,
|
|
19
|
+
sourcemap,
|
|
20
|
+
globalEsbuild = {},
|
|
21
|
+
entryEsbuild = {},
|
|
22
|
+
projectRoot,
|
|
23
|
+
} ) {
|
|
24
|
+
const defaults = getDefaultEsbuildOptions( { minify, sourcemap } );
|
|
25
|
+
const merged = {
|
|
26
|
+
...defaults,
|
|
27
|
+
...globalEsbuild,
|
|
28
|
+
...entryEsbuild,
|
|
29
|
+
loader: {
|
|
30
|
+
...defaults.loader,
|
|
31
|
+
...globalEsbuild.loader,
|
|
32
|
+
...entryEsbuild.loader,
|
|
33
|
+
},
|
|
34
|
+
define: {
|
|
35
|
+
...defaults.define,
|
|
36
|
+
...globalEsbuild.define,
|
|
37
|
+
...entryEsbuild.define,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
if ( merged.alias && projectRoot ) {
|
|
42
|
+
merged.alias = Object.fromEntries(
|
|
43
|
+
Object.entries( merged.alias ).map( ( [ key, value ] ) => [
|
|
44
|
+
key,
|
|
45
|
+
path.isAbsolute( value ) ? value : path.join( projectRoot, value ),
|
|
46
|
+
] )
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return merged;
|
|
51
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize raw merged config into executable entries + watch metadata.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { defaultProjectConfig } from './config.mjs';
|
|
7
|
+
|
|
8
|
+
const SCRIPT_GLOB = '*.{js,jsx,mjs,ts,tsx}';
|
|
9
|
+
const MODULE_GLOB = '**/*.{js,jsx,mjs,ts,tsx}';
|
|
10
|
+
const SCSS_GLOB = '*.{scss,sass}';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {object|object[]|undefined} value
|
|
14
|
+
* @param {object} defaults
|
|
15
|
+
* @return {object[]}
|
|
16
|
+
*/
|
|
17
|
+
function toEntryArray( value, defaults ) {
|
|
18
|
+
if ( ! value ) {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if ( Array.isArray( value ) ) {
|
|
23
|
+
return value.map( ( entry, index ) => ( {
|
|
24
|
+
...defaults,
|
|
25
|
+
...entry,
|
|
26
|
+
name: entry.name || `${ defaults.type }-${ index }`,
|
|
27
|
+
} ) );
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return [
|
|
31
|
+
{
|
|
32
|
+
...defaults,
|
|
33
|
+
...value,
|
|
34
|
+
name: value.name || defaults.type,
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {object} raw
|
|
41
|
+
* @param {string} srcDir
|
|
42
|
+
* @param {string} outDir
|
|
43
|
+
* @return {object}
|
|
44
|
+
*/
|
|
45
|
+
function applyRootDirs( raw, srcDir, outDir ) {
|
|
46
|
+
const mapSection = ( section, defaults, relativeSrc, relativeOut ) => {
|
|
47
|
+
if ( Array.isArray( section ) ) {
|
|
48
|
+
return section.map( ( entry ) => ( {
|
|
49
|
+
...entry,
|
|
50
|
+
src: entry.src ?? `${ srcDir }/${ relativeSrc }`,
|
|
51
|
+
out: entry.out ?? `${ outDir }/${ relativeOut }`,
|
|
52
|
+
} ) );
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const merged = {
|
|
56
|
+
...defaults,
|
|
57
|
+
...section,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const userProvidedSrc = section && typeof section === 'object' && 'src' in section;
|
|
61
|
+
const userProvidedOut = section && typeof section === 'object' && 'out' in section;
|
|
62
|
+
|
|
63
|
+
if ( ! userProvidedSrc && ( ! merged.src || merged.src === defaults.src ) ) {
|
|
64
|
+
merged.src = `${ srcDir }/${ relativeSrc }`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if ( ! userProvidedOut && ( ! merged.out || merged.out === defaults.out ) ) {
|
|
68
|
+
merged.out = `${ outDir }/${ relativeOut }`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return merged;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
...raw,
|
|
76
|
+
srcDir,
|
|
77
|
+
outDir,
|
|
78
|
+
blocks: mapSection( raw.blocks, defaultProjectConfig.blocks, 'blocks', 'blocks' ),
|
|
79
|
+
js: mapSection( raw.js, defaultProjectConfig.js, 'js', 'js' ),
|
|
80
|
+
modules: mapSection(
|
|
81
|
+
raw.modules,
|
|
82
|
+
defaultProjectConfig.modules,
|
|
83
|
+
'js/modules',
|
|
84
|
+
'js/modules'
|
|
85
|
+
),
|
|
86
|
+
scss: mapSection( raw.scss, defaultProjectConfig.scss, 'scss', 'css' ),
|
|
87
|
+
blocksManifest: {
|
|
88
|
+
...defaultProjectConfig.blocksManifest,
|
|
89
|
+
...raw.blocksManifest,
|
|
90
|
+
input:
|
|
91
|
+
raw.blocksManifest?.input === defaultProjectConfig.blocksManifest.input
|
|
92
|
+
? `${ outDir }/blocks`
|
|
93
|
+
: raw.blocksManifest?.input ?? `${ outDir }/blocks`,
|
|
94
|
+
output:
|
|
95
|
+
raw.blocksManifest?.output === defaultProjectConfig.blocksManifest.output
|
|
96
|
+
? `${ outDir }/blocks/blocks-manifest.php`
|
|
97
|
+
: raw.blocksManifest?.output ??
|
|
98
|
+
`${ outDir }/blocks/blocks-manifest.php`,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @param {object} raw
|
|
105
|
+
* @return {object}
|
|
106
|
+
*/
|
|
107
|
+
export function normalizeConfig( raw ) {
|
|
108
|
+
const srcDir = raw.srcDir ?? defaultProjectConfig.srcDir;
|
|
109
|
+
const outDir = raw.outDir ?? defaultProjectConfig.outDir;
|
|
110
|
+
const rooted = applyRootDirs( raw, srcDir, outDir );
|
|
111
|
+
|
|
112
|
+
const withDefaults = {
|
|
113
|
+
...defaultProjectConfig,
|
|
114
|
+
...rooted,
|
|
115
|
+
blocksManifest: {
|
|
116
|
+
...defaultProjectConfig.blocksManifest,
|
|
117
|
+
...rooted.blocksManifest,
|
|
118
|
+
},
|
|
119
|
+
wordpressExternals: {
|
|
120
|
+
bundle: [],
|
|
121
|
+
external: [],
|
|
122
|
+
vendors: {},
|
|
123
|
+
...( raw.wordpressExternals || {} ),
|
|
124
|
+
},
|
|
125
|
+
esbuild: raw.esbuild || {},
|
|
126
|
+
postcss: raw.postcss ?? true,
|
|
127
|
+
rtl: raw.rtl ?? false,
|
|
128
|
+
plugins: raw.plugins || [],
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
let entries = [];
|
|
132
|
+
|
|
133
|
+
if ( Array.isArray( withDefaults.entries ) && withDefaults.entries.length > 0 ) {
|
|
134
|
+
entries = withDefaults.entries.map( ( entry, index ) => ( {
|
|
135
|
+
enabled: true,
|
|
136
|
+
...entry,
|
|
137
|
+
name: entry.name || `entry-${ index }`,
|
|
138
|
+
} ) );
|
|
139
|
+
} else {
|
|
140
|
+
entries = [
|
|
141
|
+
...toEntryArray( withDefaults.blocks, {
|
|
142
|
+
type: 'blocks',
|
|
143
|
+
src: `${ srcDir }/blocks`,
|
|
144
|
+
out: `${ outDir }/blocks`,
|
|
145
|
+
discover: '*/block.json',
|
|
146
|
+
copy: [ 'block.json', 'render.php' ],
|
|
147
|
+
} ),
|
|
148
|
+
...toEntryArray( withDefaults.js, {
|
|
149
|
+
type: 'script',
|
|
150
|
+
src: `${ srcDir }/js`,
|
|
151
|
+
out: `${ outDir }/js`,
|
|
152
|
+
glob: SCRIPT_GLOB,
|
|
153
|
+
format: 'iife',
|
|
154
|
+
wordpressExternals: true,
|
|
155
|
+
assetPhp: true,
|
|
156
|
+
extractCss: false,
|
|
157
|
+
} ),
|
|
158
|
+
...toEntryArray( withDefaults.modules, {
|
|
159
|
+
type: 'script',
|
|
160
|
+
src: `${ srcDir }/js/modules`,
|
|
161
|
+
out: `${ outDir }/js/modules`,
|
|
162
|
+
glob: MODULE_GLOB,
|
|
163
|
+
format: 'esm',
|
|
164
|
+
wordpressExternals: true,
|
|
165
|
+
assetPhp: true,
|
|
166
|
+
extractCss: false,
|
|
167
|
+
} ),
|
|
168
|
+
...toEntryArray( withDefaults.scss, {
|
|
169
|
+
type: 'scss',
|
|
170
|
+
src: `${ srcDir }/scss`,
|
|
171
|
+
out: `${ outDir }/css`,
|
|
172
|
+
glob: SCSS_GLOB,
|
|
173
|
+
ignore: [ '**/_*' ],
|
|
174
|
+
} ),
|
|
175
|
+
...toEntryArray( withDefaults.copy, {
|
|
176
|
+
type: 'copy',
|
|
177
|
+
from: '',
|
|
178
|
+
to: '',
|
|
179
|
+
flatten: false,
|
|
180
|
+
} ),
|
|
181
|
+
];
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
entries = entries.filter( ( entry ) => entry.enabled !== false );
|
|
185
|
+
|
|
186
|
+
const watchPaths = [];
|
|
187
|
+
const entryWatchMap = new Map();
|
|
188
|
+
|
|
189
|
+
for ( const entry of entries ) {
|
|
190
|
+
const paths = getEntryWatchPaths( entry );
|
|
191
|
+
entryWatchMap.set( entry.name, paths );
|
|
192
|
+
for ( const watchPath of paths ) {
|
|
193
|
+
if ( ! watchPaths.includes( watchPath ) ) {
|
|
194
|
+
watchPaths.push( watchPath );
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
for ( const plugin of withDefaults.plugins ) {
|
|
200
|
+
if ( Array.isArray( plugin.watch ) ) {
|
|
201
|
+
for ( const watchPath of plugin.watch ) {
|
|
202
|
+
if ( ! watchPaths.includes( watchPath ) ) {
|
|
203
|
+
watchPaths.push( watchPath );
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
...withDefaults,
|
|
211
|
+
entries,
|
|
212
|
+
watchPaths,
|
|
213
|
+
entryWatchMap,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* @param {object} entry
|
|
219
|
+
* @return {string[]}
|
|
220
|
+
*/
|
|
221
|
+
function getEntryWatchPaths( entry ) {
|
|
222
|
+
switch ( entry.type ) {
|
|
223
|
+
case 'blocks':
|
|
224
|
+
return [ entry.src ];
|
|
225
|
+
case 'script':
|
|
226
|
+
case 'scss':
|
|
227
|
+
return [ entry.src ];
|
|
228
|
+
case 'copy': {
|
|
229
|
+
const from = entry.from || '';
|
|
230
|
+
if ( /[*?[\]]/.test( from ) ) {
|
|
231
|
+
const globRoot = from.split( /[*?[\]]/ )[ 0 ].replace( /\/$/, '' );
|
|
232
|
+
return [ globRoot || '.' ];
|
|
233
|
+
}
|
|
234
|
+
return [ from ];
|
|
235
|
+
}
|
|
236
|
+
default:
|
|
237
|
+
return entry.src ? [ entry.src ] : [];
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* @param {string} changedPath
|
|
243
|
+
* @param {string} projectRoot
|
|
244
|
+
* @param {object} normalizedConfig
|
|
245
|
+
* @return {object[]}
|
|
246
|
+
*/
|
|
247
|
+
export function getEntriesForChangedPath( changedPath, projectRoot, normalizedConfig ) {
|
|
248
|
+
const relativePath = path.relative( projectRoot, changedPath ).replace( /\\/g, '/' );
|
|
249
|
+
const affected = [];
|
|
250
|
+
|
|
251
|
+
for ( const entry of normalizedConfig.entries ) {
|
|
252
|
+
const watchPaths = normalizedConfig.entryWatchMap.get( entry.name ) || [];
|
|
253
|
+
const matches = watchPaths.some( ( watchPath ) => {
|
|
254
|
+
const normalizedWatch = watchPath.replace( /\\/g, '/' ).replace( /\/$/, '' );
|
|
255
|
+
return (
|
|
256
|
+
relativePath === normalizedWatch ||
|
|
257
|
+
relativePath.startsWith( `${ normalizedWatch }/` )
|
|
258
|
+
);
|
|
259
|
+
} );
|
|
260
|
+
|
|
261
|
+
if ( matches ) {
|
|
262
|
+
affected.push( entry );
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return affected.length > 0 ? affected : normalizedConfig.entries;
|
|
267
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Export JSON-serializable values as PHP array literals.
|
|
3
|
+
*
|
|
4
|
+
* @param {unknown} value
|
|
5
|
+
* @param {string} indent
|
|
6
|
+
* @return {string}
|
|
7
|
+
*/
|
|
8
|
+
export function exportToPhp( value, indent = '\t' ) {
|
|
9
|
+
if ( value === null ) {
|
|
10
|
+
return 'null';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if ( typeof value === 'boolean' ) {
|
|
14
|
+
return value ? 'true' : 'false';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if ( typeof value === 'number' ) {
|
|
18
|
+
return Number.isFinite( value ) ? String( value ) : 'null';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if ( typeof value === 'string' ) {
|
|
22
|
+
return `'${ value
|
|
23
|
+
.replace( /\\/g, '\\\\' )
|
|
24
|
+
.replace( /'/g, "\\'" ) }'`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if ( Array.isArray( value ) ) {
|
|
28
|
+
if ( value.length === 0 ) {
|
|
29
|
+
return 'array()';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const childIndent = indent + '\t';
|
|
33
|
+
const items = value
|
|
34
|
+
.map( ( item ) => `${ childIndent }${ exportToPhp( item, childIndent ) }` )
|
|
35
|
+
.join( ',\n' );
|
|
36
|
+
|
|
37
|
+
return `array(\n${ items }\n${ indent })`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if ( typeof value === 'object' ) {
|
|
41
|
+
const entries = Object.entries( value );
|
|
42
|
+
|
|
43
|
+
if ( entries.length === 0 ) {
|
|
44
|
+
return 'array()';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const childIndent = indent + '\t';
|
|
48
|
+
const items = entries
|
|
49
|
+
.map(
|
|
50
|
+
( [ key, item ] ) =>
|
|
51
|
+
`${ childIndent }'${ String( key ).replace( /'/g, "\\'" ) }' => ${ exportToPhp(
|
|
52
|
+
item,
|
|
53
|
+
childIndent
|
|
54
|
+
) }`
|
|
55
|
+
)
|
|
56
|
+
.join( ',\n' );
|
|
57
|
+
|
|
58
|
+
return `array(\n${ items }\n${ indent })`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return 'null';
|
|
62
|
+
}
|
package/lib/postcss.mjs
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load PostCSS plugins from project config or postcss.config.*.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { pathToFileURL } from 'url';
|
|
7
|
+
import autoprefixer from 'autoprefixer';
|
|
8
|
+
import { fileExists } from './utils.mjs';
|
|
9
|
+
|
|
10
|
+
/** @type {Map<string, import('postcss').AcceptedPlugin[]|false>} */
|
|
11
|
+
const cache = new Map();
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} projectRoot
|
|
15
|
+
* @param {boolean|import('postcss').AcceptedPlugin[]|object} postcssConfig
|
|
16
|
+
* @return {Promise<import('postcss').AcceptedPlugin[]|false>}
|
|
17
|
+
*/
|
|
18
|
+
export async function loadPostcssPlugins( projectRoot, postcssConfig = true ) {
|
|
19
|
+
const cacheKey = `${ projectRoot }:${ JSON.stringify( postcssConfig ) }`;
|
|
20
|
+
if ( cache.has( cacheKey ) ) {
|
|
21
|
+
return cache.get( cacheKey );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if ( postcssConfig === false ) {
|
|
25
|
+
cache.set( cacheKey, false );
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if ( Array.isArray( postcssConfig ) ) {
|
|
30
|
+
cache.set( cacheKey, postcssConfig );
|
|
31
|
+
return postcssConfig;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for ( const fileName of [
|
|
35
|
+
'postcss.config.mjs',
|
|
36
|
+
'postcss.config.js',
|
|
37
|
+
'postcss.config.cjs',
|
|
38
|
+
] ) {
|
|
39
|
+
const configPath = path.join( projectRoot, fileName );
|
|
40
|
+
if ( await fileExists( configPath ) ) {
|
|
41
|
+
const module = await import( pathToFileURL( configPath ).href );
|
|
42
|
+
const config = module.default ?? module;
|
|
43
|
+
const plugins =
|
|
44
|
+
typeof config === 'function'
|
|
45
|
+
? config( { env: process.env.NODE_ENV || 'development' } ).plugins
|
|
46
|
+
: config.plugins;
|
|
47
|
+
|
|
48
|
+
if ( Array.isArray( plugins ) ) {
|
|
49
|
+
cache.set( cacheKey, plugins );
|
|
50
|
+
return plugins;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const fallback = [ autoprefixer ];
|
|
56
|
+
cache.set( cacheKey, fallback );
|
|
57
|
+
return fallback;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {string} css
|
|
62
|
+
* @param {import('postcss').AcceptedPlugin[]|false} plugins
|
|
63
|
+
* @return {Promise<string>}
|
|
64
|
+
*/
|
|
65
|
+
export async function postprocessCss( css, plugins ) {
|
|
66
|
+
if ( plugins === false ) {
|
|
67
|
+
return css;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const postcss = ( await import( 'postcss' ) ).default;
|
|
71
|
+
const result = await postcss( plugins ).process( css, { from: undefined } );
|
|
72
|
+
return result.css;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {string} css
|
|
77
|
+
* @return {Promise<string>}
|
|
78
|
+
*/
|
|
79
|
+
export async function generateRtlCss( css ) {
|
|
80
|
+
const rtlcss = ( await import( 'rtlcss' ) ).default;
|
|
81
|
+
return rtlcss.process( css );
|
|
82
|
+
}
|