@plaudit/webpack-extensions 2.60.0 → 2.60.2
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.md +13 -0
- package/README.md +90 -1
- package/build/plugins/BlockJSONManagingPlugin.js +1 -1
- package/build/plugins/PlainEntrypointsConfigFileGeneratorPlugin.js +3 -3
- package/build/plugins/SpecialAssetHandlingPlugin.js +1 -1
- package/build/utils/php-writer.d.ts +23 -10
- package/build/utils/php-writer.js +114 -39
- package/package.json +1 -1
package/LICENSE.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# License
|
|
2
|
+
|
|
3
|
+
Unless otherwise stated in a written and signed agreement with Plaudit, the following license terms apply to this software:
|
|
4
|
+
* **Definitions**: ‘Client’ means the legal entity that has engaged Plaudit under a signed Master Services Agreement (MSA) or similar written contract for a project with which this library is provided. ‘Authorized Vendors’ means Client’s employees, contractors, and third‑party service providers who access this library solely on Client’s behalf.
|
|
5
|
+
* **Use**: This library shall remain the exclusive property of Plaudit. Plaudit hereby grants the Client a nonexclusive, nontransferable, worldwide license to use this library only as necessary to use and maintain the final deliverables provided by Plaudit for Client. Authorized Vendors obtain no rights independent of Client, may not sublicense or transfer, and must comply with these restrictions. Client and Authorized Vendors may not directly or indirectly create derivative works or use the library for any other client or project.
|
|
6
|
+
* **Access and Transfer**: Access by Authorized Vendors is permitted only while performing services for Client on the relevant deliverables and must be subject to written obligations no less protective than these terms. Access ends when such services end.
|
|
7
|
+
* **Warranty Disclaimer**: This software is provided **“as is”** without warranty of any kind. Plaudit disclaims all warranties, express or implied, including but not limited to warranties of merchantability, fitness for a particular purpose, and non-infringement.
|
|
8
|
+
* **Limitation of Liability**: In no event shall Plaudit be liable for any damages, liabilities, costs, losses, or expenses arising out of or in connection with the use of this software, including but not limited to incidental, consequential, or punitive damages, even if advised of the possibility of such damages.
|
|
9
|
+
* **Support and Updates**: No support, maintenance, or future updates are provided under this license. The license does not grant rights to future versions of this software.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
(License revision: 1.0)
|
package/README.md
CHANGED
|
@@ -1,5 +1,94 @@
|
|
|
1
|
+
# Plaudit Webpack Extensions
|
|
2
|
+
A webpack configuration and plugin suite designed specifically for WordPress development workflows. This package extends `@wordpress/scripts` with custom webpack plugins, PostCSS configurations, and build optimizations tailored for Plaudit's WordPress projects. It includes specialized handling for Gutenberg blocks, asset management, internationalization (WPML), and development tooling like BrowserSync integration.
|
|
3
|
+
|
|
4
|
+
# Requirements
|
|
5
|
+
- All block- and extension-related capabilities require that the `plaudit/plaudit-common` composer library be installed.
|
|
6
|
+
- All assets must be under a unified root directory and must be emitted to a unified output directory
|
|
7
|
+
- Ex: `src/blocks` and `src/extensions` emitting to `dist/blocks` and `dist/extensions`
|
|
8
|
+
|
|
9
|
+
# Perks
|
|
10
|
+
## Truly automatic asset loading
|
|
11
|
+
With the `useUnifiedLoader` flag enabled (see the usage example), this package will generate a single file that, when included, automatically registers and enqueues scripts, styles, and assets.
|
|
12
|
+
This includes prefetching assets when the `useWebpackResourceFiltering` flag is enabled.
|
|
13
|
+
|
|
14
|
+
## Automatic filename mapping in `block.json`
|
|
15
|
+
Instead of requiring developers to plug in the built names of files when developing blocks, this suite will automatically replace the source name with the built name in emitted block.json files *and* will automatically resolve filename conflicts that would otherwise cause the build to fail.
|
|
16
|
+
|
|
17
|
+
In addition to handling filename conflicts, it also automatically detects the presence of `template.php` and `template.twig` files and ensures that they are properly registered in the emitted block.json files.
|
|
18
|
+
|
|
19
|
+
## Efficient loading
|
|
20
|
+
This suite generates loader code that uses the most efficient mechanisms available for registering assets, and, in the case of blocks in themes, a loading mechanism that is more efficient than any "available" method.
|
|
21
|
+
|
|
22
|
+
## Unified Plugin and Theme Structures
|
|
23
|
+
The same file structure (both in the webpack.config.js file and the actual directory tree) can be used in plugins and themes.
|
|
24
|
+
|
|
25
|
+
# Usage
|
|
26
|
+
## An Example Config
|
|
27
|
+
NOTE: This config assumes that `require_once __DIR__.'/dist/unified-loader.php';` has been added to the theme's functions.php file or the plugin's main PHP file
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
module.exports = require("@plaudit/webpack-extensions/wordpress-scripts-wrapper")({
|
|
31
|
+
src: {
|
|
32
|
+
"blocks": true, // By default, a directory is assumed to contain WordPress blocks, so we don't need to do anything other than tell WebPack to include it
|
|
33
|
+
"extensions": { // Extensions need to have their directory layout specified, but the loader is automatically generated, so there's no need to specify an output path
|
|
34
|
+
directoryLayout: 'extensions'
|
|
35
|
+
},
|
|
36
|
+
"site/index-header.ts": { // "Plain" entrypoints (i.e. those not associated with a block or extension) can have their usage locations specified, at which point they will also be automatically loaded
|
|
37
|
+
locations: {
|
|
38
|
+
clientView: true,
|
|
39
|
+
registerScriptArgs: false
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"site/index-footer.ts": {
|
|
43
|
+
withLegacyBlocksIn: "blocks",
|
|
44
|
+
locations: {
|
|
45
|
+
clientView: true,
|
|
46
|
+
registerScriptArgs: {strategy: "defer", in_footer: true}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"site/wp-admin.ts": {
|
|
50
|
+
locations: {
|
|
51
|
+
admin: true
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"site/wp-admin.pcss": {
|
|
55
|
+
locations: {
|
|
56
|
+
admin: true
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"site/block-editor.pcss": {
|
|
60
|
+
locations: {
|
|
61
|
+
clientEditor: true
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"site/public.pcss": {
|
|
65
|
+
locations: {
|
|
66
|
+
clientView: true
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
useWebpackResourceFiltering: true,
|
|
71
|
+
extensionsVersion: 2,
|
|
72
|
+
plainEntrypointsVersion: 2,
|
|
73
|
+
srcDir: "src",
|
|
74
|
+
outputDir: "dist",
|
|
75
|
+
useUnifiedLoader: true
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
1
79
|
# Development
|
|
2
|
-
|
|
80
|
+
|
|
81
|
+
## Setup
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
pnpm install
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
# Build
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
pnpm build
|
|
91
|
+
```
|
|
3
92
|
|
|
4
93
|
# To Deploy
|
|
5
94
|
```
|
|
@@ -329,7 +329,7 @@ class BlockJSONManagingPlugin {
|
|
|
329
329
|
.use("Plaudit\\Common\\ACF\\BlockManager")
|
|
330
330
|
.action("init", writer => {
|
|
331
331
|
writer.call("BlockManager::autoloadSubfolders", [new php_writer_1.Expr(`__DIR__.${php_writer_1.Expr.convertJsonToPHP("/" + this.blocksDest)}`)]);
|
|
332
|
-
});
|
|
332
|
+
}, { accountForAlreadyDoing: true });
|
|
333
333
|
}
|
|
334
334
|
});
|
|
335
335
|
});
|
|
@@ -182,7 +182,7 @@ class PlainEntrypointsConfigFileGeneratorPlugin {
|
|
|
182
182
|
for (const { handle, type, data } of prioritizedHandleList) {
|
|
183
183
|
writer.call(`wp_register_${type}`, [handle, new php_writer_1.Expr(`$base_uri.${php_writer_1.Expr.jsonToPHPConverter(node_path_1.default.relative(emitDir, data.src))}`), ...data.rest]);
|
|
184
184
|
}
|
|
185
|
-
}, { priority });
|
|
185
|
+
}, { priority, accountForAlreadyDoing: true });
|
|
186
186
|
}
|
|
187
187
|
const sortedEditorStyleHandles = sortedStyleHandles
|
|
188
188
|
.filter(([_, { locations: { clientEditor }, isScript }]) => !isScript && (clientEditor || typeof clientEditor === 'number'))
|
|
@@ -227,7 +227,7 @@ class PlainEntrypointsConfigFileGeneratorPlugin {
|
|
|
227
227
|
for (const [action, handleList] of enqueuingHandleActions) {
|
|
228
228
|
if (handleList.length > 0) {
|
|
229
229
|
for (const [priority, prioritizedHandleList] of PlainEntrypointsConfigFileGeneratorPlugin.separateHandleListByPriority(handleList)) {
|
|
230
|
-
writer.action(action,
|
|
230
|
+
writer.action(action, writer => {
|
|
231
231
|
for (const handle of prioritizedHandleList) {
|
|
232
232
|
writer.call(`wp_enqueue_${handle.type}`, [handle.handle]);
|
|
233
233
|
}
|
|
@@ -253,7 +253,7 @@ class PlainEntrypointsConfigFileGeneratorPlugin {
|
|
|
253
253
|
* The primary benefit of emitting a function instead of baking its contents into each function that uses it is that it allows us to avoid recomputing the base uri multiple times
|
|
254
254
|
*/
|
|
255
255
|
static emitResolveBaseUriFunction(writer) {
|
|
256
|
-
writer.function("plaudit_webpack_extensions__resolve_base_uri", ["$dir"],
|
|
256
|
+
writer.function("plaudit_webpack_extensions__resolve_base_uri", ["$dir"], writer => {
|
|
257
257
|
writer
|
|
258
258
|
.static("$base_uris", { initializer: [] })
|
|
259
259
|
.if("isset($base_uris[$dir])")
|
|
@@ -136,7 +136,7 @@ class SpecialAssetHandlingPlugin {
|
|
|
136
136
|
writer.append(`<link rel="preload" href="<?= esc_url($base_uri.'${filename}') ?>" ${dynamicAttrs}>`);
|
|
137
137
|
}
|
|
138
138
|
writer.openPHP();
|
|
139
|
-
});
|
|
139
|
+
}, { accountForAlreadyDoing: true });
|
|
140
140
|
writer.emitAsset(compilation, outputFile);
|
|
141
141
|
}
|
|
142
142
|
}
|
|
@@ -11,23 +11,31 @@ export type ActionOrFilterArgs = {
|
|
|
11
11
|
priority?: number | Expr;
|
|
12
12
|
functionArgParameters?: string[];
|
|
13
13
|
useVars?: string[];
|
|
14
|
+
accountForAlreadyDoing?: boolean | Parameters<PHPWriter['call']>[1];
|
|
14
15
|
};
|
|
15
16
|
export type FunctionCreationArgs = {
|
|
16
17
|
includeExistenceCheck?: boolean;
|
|
17
18
|
useVars?: string[];
|
|
18
19
|
returnType?: string;
|
|
20
|
+
assignToName?: boolean;
|
|
21
|
+
scopeActionDescription?: string;
|
|
19
22
|
};
|
|
20
23
|
export declare class PHPWriter {
|
|
21
24
|
private readonly inlineFirstLine;
|
|
25
|
+
private readonly scopeStack;
|
|
26
|
+
private readonly useList;
|
|
27
|
+
private readonly allocatedGeneratedFunctionNames;
|
|
28
|
+
private readonly isSubwriter;
|
|
29
|
+
private printingInPHP;
|
|
22
30
|
private buffer;
|
|
23
31
|
private indentation;
|
|
24
|
-
private printingInPHP;
|
|
25
32
|
private fileNamespace;
|
|
26
|
-
private
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
private myOpenedScopes;
|
|
34
|
+
constructor(inlineFirstLine?: boolean, scopeStack?: string[][], useList?: string[], allocatedGeneratedFunctionNames?: Set<string>, isSubwriter?: boolean, printingInPHP?: boolean, initialIndentation?: number);
|
|
35
|
+
createSubwriter(): PHPWriter;
|
|
29
36
|
indent(): this;
|
|
30
37
|
outdent(): this;
|
|
38
|
+
setIndentation(level: number): this;
|
|
31
39
|
append(...lines: (string | Expr)[]): this;
|
|
32
40
|
assign(variable: string | string[], expression: unknown | Expr, opts?: {
|
|
33
41
|
chain?: boolean;
|
|
@@ -44,14 +52,16 @@ export declare class PHPWriter {
|
|
|
44
52
|
assignTo?: string | string[];
|
|
45
53
|
return?: boolean;
|
|
46
54
|
}): this;
|
|
47
|
-
action(name: string | Expr, contents: (writer:
|
|
48
|
-
filter(name: string | Expr, contents: (writer:
|
|
49
|
-
actionOrFilter(type: 'action' | 'filter', name: string | Expr, contents: (writer:
|
|
55
|
+
action(name: string | Expr, contents: (writer: PHPWriter) => void, args?: ActionOrFilterArgs): this;
|
|
56
|
+
filter(name: string | Expr, contents: (writer: PHPWriter) => void, args?: ActionOrFilterArgs): this;
|
|
57
|
+
actionOrFilter(type: 'action' | 'filter', name: string | Expr, contents: (writer: PHPWriter) => void, args: ActionOrFilterArgs): this;
|
|
50
58
|
if(condition: string): this;
|
|
51
59
|
elseIf(condition: string): this;
|
|
52
60
|
else(): this;
|
|
53
61
|
endIf(): this;
|
|
54
|
-
function(name:
|
|
62
|
+
function(name: true, parameters: string[], body: (writer: PHPWriter) => void, args?: FunctionCreationArgs): string;
|
|
63
|
+
function(name: string | false, parameters: string[], body: (writer: PHPWriter) => void, args?: FunctionCreationArgs): this;
|
|
64
|
+
generateFunctionName(asVariable?: boolean): string;
|
|
55
65
|
closePHP(): this;
|
|
56
66
|
openPHP(): this;
|
|
57
67
|
namespace(namespace: string): this;
|
|
@@ -70,10 +80,13 @@ export declare class PHPWriter {
|
|
|
70
80
|
* This is used to allow function bodies to be safely created without disrupting their containing scope.
|
|
71
81
|
*/
|
|
72
82
|
popScope(): this;
|
|
73
|
-
withScope(code: (writer:
|
|
83
|
+
withScope(code: (writer: PHPWriter) => void, opts?: {
|
|
74
84
|
actionDescription?: string;
|
|
75
85
|
usePopScopeInstead?: boolean;
|
|
76
86
|
}): this;
|
|
77
|
-
toString(
|
|
87
|
+
toString(args?: {
|
|
88
|
+
includeOpenPHP?: boolean;
|
|
89
|
+
includeNamespaceAndUse?: boolean;
|
|
90
|
+
}): string;
|
|
78
91
|
emitAsset(compilation: Compilation, file: string, assetInfo?: AssetInfo): void;
|
|
79
92
|
}
|
|
@@ -22,14 +22,27 @@ class Expr {
|
|
|
22
22
|
exports.Expr = Expr;
|
|
23
23
|
class PHPWriter {
|
|
24
24
|
inlineFirstLine;
|
|
25
|
+
scopeStack;
|
|
26
|
+
useList;
|
|
27
|
+
allocatedGeneratedFunctionNames;
|
|
28
|
+
isSubwriter;
|
|
29
|
+
printingInPHP;
|
|
25
30
|
buffer = [];
|
|
26
|
-
indentation
|
|
27
|
-
printingInPHP = true;
|
|
31
|
+
indentation;
|
|
28
32
|
fileNamespace = "";
|
|
29
|
-
|
|
30
|
-
scopeStack = []
|
|
31
|
-
constructor(inlineFirstLine = false) {
|
|
33
|
+
myOpenedScopes = 0;
|
|
34
|
+
constructor(inlineFirstLine = false, scopeStack = [], useList = [], allocatedGeneratedFunctionNames = new Set(), isSubwriter = false, printingInPHP = true, initialIndentation = 0) {
|
|
32
35
|
this.inlineFirstLine = inlineFirstLine;
|
|
36
|
+
this.scopeStack = scopeStack;
|
|
37
|
+
this.useList = useList;
|
|
38
|
+
this.allocatedGeneratedFunctionNames = allocatedGeneratedFunctionNames;
|
|
39
|
+
this.isSubwriter = isSubwriter;
|
|
40
|
+
this.printingInPHP = printingInPHP;
|
|
41
|
+
this.indentation = "\t".repeat(initialIndentation);
|
|
42
|
+
}
|
|
43
|
+
createSubwriter() {
|
|
44
|
+
return new PHPWriter(false, this.scopeStack, this.useList, this.allocatedGeneratedFunctionNames, true, this.printingInPHP, this.indentation.length)
|
|
45
|
+
.setIndentation(this.indentation.length);
|
|
33
46
|
}
|
|
34
47
|
indent() {
|
|
35
48
|
this.indentation += "\t";
|
|
@@ -39,6 +52,10 @@ class PHPWriter {
|
|
|
39
52
|
this.indentation = this.indentation.slice(0, -1);
|
|
40
53
|
return this;
|
|
41
54
|
}
|
|
55
|
+
setIndentation(level) {
|
|
56
|
+
this.indentation = "\t".repeat(level);
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
42
59
|
append(...lines) {
|
|
43
60
|
for (const line of lines) {
|
|
44
61
|
this.buffer.push(`${this.indentation}${line}`);
|
|
@@ -103,31 +120,44 @@ class PHPWriter {
|
|
|
103
120
|
return this.actionOrFilter('filter', name, contents, args);
|
|
104
121
|
}
|
|
105
122
|
actionOrFilter(type, name, contents, args) {
|
|
106
|
-
const { priority = 10, functionArgParameters = [], useVars = [] } = args;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (
|
|
110
|
-
|
|
123
|
+
const { priority = 10, functionArgParameters = [], useVars = [], accountForAlreadyDoing } = args;
|
|
124
|
+
const functionWriter = this.createSubwriter();
|
|
125
|
+
let declarationFunctionText;
|
|
126
|
+
if (accountForAlreadyDoing) {
|
|
127
|
+
if (functionArgParameters.length > 0 && accountForAlreadyDoing === true) {
|
|
128
|
+
console.trace(`The accountForAlreadyDoing flag must be set to a list of default parameters when applied to ${type}s that take arguments`);
|
|
129
|
+
}
|
|
130
|
+
this.openPHP().openScope();
|
|
131
|
+
const functionName = functionWriter.function(true, functionArgParameters, contents, { useVars, scopeActionDescription: `closing the function call for the ${name} ${type}.`, assignToName: true });
|
|
132
|
+
this.append(functionWriter.toString().trim());
|
|
133
|
+
declarationFunctionText = functionName + "(...)";
|
|
134
|
+
this.if(`doing_${type}(${Expr.convertJsonToPHP(name)})`).call(functionName, accountForAlreadyDoing === true ? [] : accountForAlreadyDoing);
|
|
111
135
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
this.openPHP().outdent();
|
|
119
|
-
const actionOrFilterArgs = ["}"];
|
|
136
|
+
else {
|
|
137
|
+
functionWriter.function(false, functionArgParameters, contents, { useVars, scopeActionDescription: `closing the function call for the ${name} ${type}.` });
|
|
138
|
+
declarationFunctionText = functionWriter.toString().trim();
|
|
139
|
+
}
|
|
140
|
+
// The trailing comma inside the first item is necessary
|
|
141
|
+
const declarationComponents = [`${Expr.convertJsonToPHP(name)}`, declarationFunctionText];
|
|
120
142
|
const accepted_args = Math.max(functionArgParameters.length, 1); // This avoids us unnecessarily setting the accepted_args value to 0 for actions
|
|
121
143
|
if (priority !== 10) {
|
|
122
|
-
|
|
144
|
+
declarationComponents.push(priority.toString());
|
|
123
145
|
if (accepted_args !== 1) {
|
|
124
|
-
|
|
146
|
+
declarationComponents.push(accepted_args.toString());
|
|
125
147
|
}
|
|
126
148
|
}
|
|
127
149
|
else if (accepted_args !== 1) {
|
|
128
|
-
|
|
150
|
+
declarationComponents.push(`accepted_args: ${accepted_args}`);
|
|
151
|
+
}
|
|
152
|
+
const line = `add_${type}(${declarationComponents.join(", ")});`;
|
|
153
|
+
if (accountForAlreadyDoing) {
|
|
154
|
+
return this
|
|
155
|
+
.else()
|
|
156
|
+
.append(line)
|
|
157
|
+
.endIf()
|
|
158
|
+
.closeScope();
|
|
129
159
|
}
|
|
130
|
-
return this.
|
|
160
|
+
return this.openPHP().append(line);
|
|
131
161
|
}
|
|
132
162
|
if(condition) {
|
|
133
163
|
return this.openPHP().append(`if (${condition}) {`).indent();
|
|
@@ -142,14 +172,31 @@ class PHPWriter {
|
|
|
142
172
|
return this.openPHP().outdent().append("}");
|
|
143
173
|
}
|
|
144
174
|
function(name, parameters, body, args = {}) {
|
|
175
|
+
if (name === false) {
|
|
176
|
+
if (args.includeExistenceCheck) {
|
|
177
|
+
console.trace('Anonymous functions cannot have their existence checked for');
|
|
178
|
+
}
|
|
179
|
+
if (args.assignToName) {
|
|
180
|
+
console.trace('Anonymous functions cannot be assigned to a name');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const returningString = name === true;
|
|
184
|
+
if (name === true) {
|
|
185
|
+
name = this.generateFunctionName(args.assignToName);
|
|
186
|
+
}
|
|
145
187
|
if (args.includeExistenceCheck) {
|
|
146
188
|
this.if(`!function_exists(${Expr.convertJsonToPHP(name)})`);
|
|
147
189
|
}
|
|
148
190
|
else {
|
|
149
191
|
this.openPHP();
|
|
150
192
|
}
|
|
151
|
-
|
|
152
|
-
|
|
193
|
+
if (name !== false && args.assignToName) {
|
|
194
|
+
if (this.scopeStack.length > 0) {
|
|
195
|
+
this.scopeStack[this.scopeStack.length - 1].push(name);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const declarationComponents = name === false ? [] : (args.assignToName ? [name, '='] : ["function"]);
|
|
199
|
+
let nameAndParameters = `${!name || args.assignToName ? 'function' : name}(${parameters.join(", ")})`;
|
|
153
200
|
if (args.useVars?.length) {
|
|
154
201
|
let useVars = `use (${args.useVars.join(", ")})`;
|
|
155
202
|
if (args.returnType) {
|
|
@@ -168,12 +215,21 @@ class PHPWriter {
|
|
|
168
215
|
this.indent();
|
|
169
216
|
// We start a new scope here just in case this was called within an existing scope
|
|
170
217
|
// We pop the scope instead of closing it because we don't actually want to unset any variables that were created within the function
|
|
171
|
-
this.withScope(body, { actionDescription: `closing the "${name}" function`, usePopScopeInstead: true });
|
|
172
|
-
this.openPHP().outdent().append("}");
|
|
218
|
+
this.withScope(body, { actionDescription: args.scopeActionDescription ?? `closing the "${name || "anonymous"}" function`, usePopScopeInstead: true });
|
|
219
|
+
this.openPHP().outdent().append(name !== false && args.assignToName ? "};" : "}");
|
|
173
220
|
if (args.includeExistenceCheck) {
|
|
174
|
-
|
|
221
|
+
this.endIf();
|
|
175
222
|
}
|
|
176
|
-
return this;
|
|
223
|
+
return returningString ? name : this;
|
|
224
|
+
}
|
|
225
|
+
generateFunctionName(asVariable = false) {
|
|
226
|
+
let count = 0;
|
|
227
|
+
let name;
|
|
228
|
+
do {
|
|
229
|
+
name = `${asVariable ? '$' : ''}plaudit_webpack_extensions__generated_function__${count++}`;
|
|
230
|
+
} while (this.allocatedGeneratedFunctionNames.has(name));
|
|
231
|
+
this.allocatedGeneratedFunctionNames.add(name);
|
|
232
|
+
return name;
|
|
177
233
|
}
|
|
178
234
|
closePHP() {
|
|
179
235
|
if (!this.printingInPHP) {
|
|
@@ -207,21 +263,35 @@ class PHPWriter {
|
|
|
207
263
|
*/
|
|
208
264
|
openScope() {
|
|
209
265
|
this.scopeStack.push([]);
|
|
266
|
+
this.myOpenedScopes++;
|
|
210
267
|
return this;
|
|
211
268
|
}
|
|
212
269
|
/**
|
|
213
270
|
* Pops the top scope from the stack AND unsets all variables that were assigned within it
|
|
214
271
|
*/
|
|
215
272
|
closeScope() {
|
|
273
|
+
if (!this.myOpenedScopes) {
|
|
274
|
+
return this;
|
|
275
|
+
}
|
|
276
|
+
this.myOpenedScopes--;
|
|
216
277
|
const scope = this.scopeStack.pop();
|
|
217
|
-
|
|
278
|
+
if (!scope?.length) {
|
|
279
|
+
return this;
|
|
280
|
+
}
|
|
281
|
+
for (const scopeItem of scope) {
|
|
282
|
+
this.allocatedGeneratedFunctionNames.delete(scopeItem);
|
|
283
|
+
}
|
|
284
|
+
return this.call("unset", scope.map(v => new Expr(v)));
|
|
218
285
|
}
|
|
219
286
|
/**
|
|
220
287
|
* Pops the top scope from the stack WITHOUT calling unset for the variables that were assigned within it.
|
|
221
288
|
* This is used to allow function bodies to be safely created without disrupting their containing scope.
|
|
222
289
|
*/
|
|
223
290
|
popScope() {
|
|
224
|
-
this.
|
|
291
|
+
if (this.myOpenedScopes) {
|
|
292
|
+
this.scopeStack.pop();
|
|
293
|
+
this.myOpenedScopes--;
|
|
294
|
+
}
|
|
225
295
|
return this;
|
|
226
296
|
}
|
|
227
297
|
withScope(code, opts = {}) {
|
|
@@ -239,21 +309,26 @@ class PHPWriter {
|
|
|
239
309
|
}
|
|
240
310
|
return this;
|
|
241
311
|
}
|
|
242
|
-
toString() {
|
|
243
|
-
if (this.
|
|
312
|
+
toString(args = this.isSubwriter ? { includeOpenPHP: false, includeNamespaceAndUse: false } : {}) {
|
|
313
|
+
if (this.myOpenedScopes) {
|
|
244
314
|
console.trace("toString() was called on a PHPWriter that has a dangling scope");
|
|
245
315
|
}
|
|
246
316
|
const fileContents = [];
|
|
247
317
|
let canInline = true;
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
318
|
+
if (args.includeNamespaceAndUse !== false) {
|
|
319
|
+
if (this.fileNamespace) {
|
|
320
|
+
canInline = false;
|
|
321
|
+
fileContents.push(`namespace ${this.fileNamespace};`);
|
|
322
|
+
}
|
|
323
|
+
if (this.useList.length) {
|
|
324
|
+
canInline = false;
|
|
325
|
+
fileContents.push(...this.useList.map(use => `use ${use};`));
|
|
326
|
+
}
|
|
255
327
|
}
|
|
256
328
|
fileContents.push(...this.buffer);
|
|
329
|
+
if (args.includeOpenPHP === false) {
|
|
330
|
+
return fileContents.join("\n");
|
|
331
|
+
}
|
|
257
332
|
if (this.inlineFirstLine && canInline) {
|
|
258
333
|
return `<?php ${fileContents.join("\n")}`;
|
|
259
334
|
}
|