@ideasonpurpose/build-tools-wordpress 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/.github/workflows/npm-publish.yml +23 -0
- package/.github/workflows/release-from-version-tag.yml +32 -0
- package/.vscode/settings.json +22 -0
- package/LICENSE +21 -0
- package/README.md +54 -0
- package/bin/port-reporter.js +22 -0
- package/index.js +16 -0
- package/lib/AfterDoneReporterPlugin.js +50 -0
- package/lib/DependencyManifestPlugin.js +77 -0
- package/lib/ImageminPlugins.js +60 -0
- package/lib/WatchRunReporterPlugin.js +54 -0
- package/lib/buildConfig.js +211 -0
- package/lib/devserver-proxy.js +166 -0
- package/lib/prettier-hrtime.js +16 -0
- package/package.json +88 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Publish to NPM
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
name: Build and publish to npm
|
|
10
|
+
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
|
|
11
|
+
runs-on: ubuntu-22.04
|
|
12
|
+
steps:
|
|
13
|
+
# https://github.com/marketplace/actions/checkout
|
|
14
|
+
- uses: actions/checkout@v3
|
|
15
|
+
# https://github.com/actions/setup-node
|
|
16
|
+
- uses: actions/setup-node@v3
|
|
17
|
+
with:
|
|
18
|
+
node-version: 18
|
|
19
|
+
registry-url: https://registry.npmjs.org/
|
|
20
|
+
# - run: npm ci
|
|
21
|
+
- run: npm publish
|
|
22
|
+
env:
|
|
23
|
+
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Create Release from Version Tags
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
name: Build Release
|
|
10
|
+
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
|
|
11
|
+
runs-on: ubuntu-22.04
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
# https://github.com/marketplace/actions/checkout
|
|
15
|
+
- uses: actions/checkout@v3
|
|
16
|
+
|
|
17
|
+
- name: Set up REPO and TAG environment vars
|
|
18
|
+
run: |
|
|
19
|
+
echo "REPO=${GITHUB_REPOSITORY#*/}" >> $GITHUB_ENV
|
|
20
|
+
echo "TAG=${GITHUB_SHA:0:6}" >> $GITHUB_ENV
|
|
21
|
+
|
|
22
|
+
- name: This run was triggered by a version tag, reset the $TAG variable to the tag name
|
|
23
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
24
|
+
run: |
|
|
25
|
+
echo "TAG=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
|
26
|
+
|
|
27
|
+
- name: Create GitHub release
|
|
28
|
+
if: ${{ contains(github.ref, 'refs/tags/') }}
|
|
29
|
+
env:
|
|
30
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
31
|
+
run: |
|
|
32
|
+
gh release create v${TAG}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"workbench.colorCustomizations": {
|
|
3
|
+
"activityBar.activeBackground": "#ffc1dc",
|
|
4
|
+
"activityBar.background": "#ffc1dc",
|
|
5
|
+
"activityBar.foreground": "#15202b",
|
|
6
|
+
"activityBar.inactiveForeground": "#15202b99",
|
|
7
|
+
"activityBarBadge.background": "#459f00",
|
|
8
|
+
"activityBarBadge.foreground": "#e7e7e7",
|
|
9
|
+
"commandCenter.border": "#15202b99",
|
|
10
|
+
"sash.hoverBorder": "#ffc1dc",
|
|
11
|
+
"statusBar.background": "#ff8ebf",
|
|
12
|
+
"statusBar.foreground": "#15202b",
|
|
13
|
+
"statusBarItem.hoverBackground": "#ff5ba2",
|
|
14
|
+
"statusBarItem.remoteBackground": "#ff8ebf",
|
|
15
|
+
"statusBarItem.remoteForeground": "#15202b",
|
|
16
|
+
"titleBar.activeBackground": "#ff8ebf",
|
|
17
|
+
"titleBar.activeForeground": "#15202b",
|
|
18
|
+
"titleBar.inactiveBackground": "#ff8ebf99",
|
|
19
|
+
"titleBar.inactiveForeground": "#15202b99"
|
|
20
|
+
},
|
|
21
|
+
"peacock.color": "#ff8ebf"
|
|
22
|
+
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Ideas On Purpose
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# @ideasonpurpose/build-tools-wordpress
|
|
2
|
+
|
|
3
|
+
#### Version 1.1.0
|
|
4
|
+
|
|
5
|
+
Build scripts and dependencies for IOP's WordPress development environments.
|
|
6
|
+
|
|
7
|
+
## About This Project
|
|
8
|
+
|
|
9
|
+
These tools were migrated from our [Docker-based WordPress build tools](https://github.com/ideasonpurpose/docker-build) to speed up development and began the process of moving our build tools away from webpack. Gathering dependencies also simplifies the package.json files in host projects, making those slightly more manageable.
|
|
10
|
+
|
|
11
|
+
This may end up being the container for all tools required for building a project. That would allow us to clean out most of the cruft from our project directories and only have a single dependency which packages everything we need.
|
|
12
|
+
|
|
13
|
+
The included webpack.config.js file works best when paired with a PHP environment like our [Docker WordPress environments](https://github.com/ideasonpurpose/docker-wordpress-dev). It's capable of proxying to other servers, but that's sort of crazy.
|
|
14
|
+
|
|
15
|
+
### Additional Notes
|
|
16
|
+
|
|
17
|
+
This project expects an entirely ES Module based environment and specifies all dependencies using standard ESM import syntax. Projects importing this file should set `"type": "module"` in their package.json files.
|
|
18
|
+
|
|
19
|
+
##
|
|
20
|
+
|
|
21
|
+
#### Brought to you by IOP
|
|
22
|
+
|
|
23
|
+
<a href="https://www.ideasonpurpose.com"><img src="https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/iop-logo-mint-on-black-88px.png" height="44" align="top" alt="IOP Logo"></a><img src="https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/spacer.png" align="middle" width="4" height="54"> This project is actively developed and used in production at <a href="https://www.ideasonpurpose.com">Ideas On Purpose</a>.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
#### Brought to you by IOP
|
|
27
|
+
|
|
28
|
+
<a href="https://www.ideasonpurpose.com"><img src="https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/iop-logo-white-on-black-88px.png" height="44" align="top" alt="IOP Logo"></a><img src="https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/spacer.png" align="middle" width="4" height="54"> This project is actively developed and used in production at <a href="https://www.ideasonpurpose.com">Ideas On Purpose</a>.
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
#### Brought to you by IOP
|
|
32
|
+
|
|
33
|
+
<a href="https://www.ideasonpurpose.com"><img src="https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/iop-logo-clay-on-black-88px.png" height="44" align="top" alt="IOP Logo"></a><img src="https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/spacer.png" align="middle" width="4" height="54"> This project is actively developed and used in production at <a href="https://www.ideasonpurpose.com">Ideas On Purpose</a>.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
#### Brought to you by IOP
|
|
37
|
+
|
|
38
|
+
<a href="https://www.ideasonpurpose.com"><img src="https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/iop-logo-white-on-mint-88px.png" height="44" align="top" alt="IOP Logo"></a><img src="https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/spacer.png" align="middle" width="4" height="54"> This project is actively developed and used in production at <a href="https://www.ideasonpurpose.com">Ideas On Purpose</a>.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
#### Brought to you by IOP
|
|
43
|
+
|
|
44
|
+
<a href="https://www.ideasonpurpose.com"><img src="https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/iop-logo-black-on-mint-88px.png" height="44" align="top" alt="IOP Logo"></a><img src="https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/spacer.png" align="middle" width="4" height="54"> This project is actively developed and used in production at <a href="https://www.ideasonpurpose.com">Ideas On Purpose</a>.
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
#### Brought to you by IOP
|
|
49
|
+
|
|
50
|
+
<a href="https://www.ideasonpurpose.com"><img src="https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/iop-logo-white-on-darkmint-88px.png" height="44" align="top" alt="IOP Logo"></a><img src="https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/spacer.png" align="middle" width="4" height="54"> This project is actively developed and used in production at <a href="https://www.ideasonpurpose.com">Ideas On Purpose</a>.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
> *logo test...*
|
|
54
|
+
>😂
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
import { exec } from "node:child_process";
|
|
3
|
+
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
|
|
6
|
+
exec("docker compose port wordpress 80", (error, stdout, stderr) => {
|
|
7
|
+
if (error) {
|
|
8
|
+
console.error(`exec error: ${error}`);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (stdout) {
|
|
12
|
+
const [addr, port] = stdout.split(":");
|
|
13
|
+
|
|
14
|
+
console.log("\n",
|
|
15
|
+
" 🚀 ",
|
|
16
|
+
chalk.bold( "Local WP site:"),
|
|
17
|
+
chalk.magenta(`http://localhost:${chalk.bold(port)}`),
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
// console.log(`stdout: ${stdout}`);
|
|
21
|
+
// console.error(`stderr: ${stderr}`);
|
|
22
|
+
});
|
package/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import AfterDoneReporterPlugin from "./lib/AfterDoneReporterPlugin.js";
|
|
2
|
+
import buildConfig from "./lib/buildConfig.js";
|
|
3
|
+
import DependencyManifestPlugin from "./lib/DependencyManifestPlugin.js";
|
|
4
|
+
import devserverProxy from "./lib/devserver-proxy.js";
|
|
5
|
+
import WatchRunReporterPlugin from "./lib/WatchRunReporterPlugin.js";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
// export { };
|
|
10
|
+
export {
|
|
11
|
+
AfterDoneReporterPlugin,
|
|
12
|
+
buildConfig,
|
|
13
|
+
DependencyManifestPlugin,
|
|
14
|
+
devserverProxy,
|
|
15
|
+
WatchRunReporterPlugin,
|
|
16
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import humanizeDuration from "humanize-duration";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Simple plugin for printing some text after compilation stats are displayed
|
|
6
|
+
*
|
|
7
|
+
* Messages should be configured in an argument object:
|
|
8
|
+
*
|
|
9
|
+
* new AfterDoneReporterPlugin({message: "Your Message Here"})
|
|
10
|
+
*/
|
|
11
|
+
export default class AfterDoneReporterPlugin {
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
const defaults = {
|
|
14
|
+
echo: true,
|
|
15
|
+
prefix: `🟢${chalk.gray("(iop)")}:`,
|
|
16
|
+
message: "",
|
|
17
|
+
};
|
|
18
|
+
this.config = { ...defaults, ...options };
|
|
19
|
+
this.name = "IOP Reporter Plugin";
|
|
20
|
+
this.messages = [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
apply(compiler) {
|
|
24
|
+
compiler.hooks.done.tapPromise(this.name, async (stats) => {
|
|
25
|
+
if (this.config.echo) {
|
|
26
|
+
const { host, port } = compiler.options.devServer;
|
|
27
|
+
const { startTime, endTime, modules, assetsInfo } = stats.compilation;
|
|
28
|
+
const hostname = host === "0.0.0.0" ? "localhost" : host;
|
|
29
|
+
|
|
30
|
+
const time = chalk.yellow.bold(
|
|
31
|
+
humanizeDuration(endTime - startTime, { units: ["h", "m", "s"] })
|
|
32
|
+
);
|
|
33
|
+
const mCount = chalk.yellow(modules.size);
|
|
34
|
+
const aCount = chalk.yellow.bold(assetsInfo.size);
|
|
35
|
+
|
|
36
|
+
const messages = [
|
|
37
|
+
`Compiled ${mCount} input modules into ${aCount} files in ${time}`,
|
|
38
|
+
"Dev site " + chalk.blue(`http://${hostname}:${chalk.bold(port)}`),
|
|
39
|
+
this.config.message,
|
|
40
|
+
]
|
|
41
|
+
.filter((m) => m.length)
|
|
42
|
+
.join("\n" + this.config.prefix);
|
|
43
|
+
|
|
44
|
+
setTimeout(() =>
|
|
45
|
+
console.log("\n" + this.config.prefix + " " + messages)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
class DependencyManifestPlugin {
|
|
5
|
+
constructor(options) {
|
|
6
|
+
const defaults = {
|
|
7
|
+
writeManifestFile: false,
|
|
8
|
+
manifestFile: "./dependency-manifest.json",
|
|
9
|
+
};
|
|
10
|
+
this.config = { ...defaults, ...options };
|
|
11
|
+
this.name = "Dependency Manifest Plugin";
|
|
12
|
+
this.manifest = {};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
apply(compiler) {
|
|
16
|
+
compiler.hooks.emit.tapAsync(this.name, (compilation, callback) => {
|
|
17
|
+
// const logger = compilation.getLogger("dependency-manifest-plugin");
|
|
18
|
+
|
|
19
|
+
// logger.info(compilation.namedChunkGroups.keys());
|
|
20
|
+
|
|
21
|
+
Array.from(compilation.namedChunkGroups.entries()).forEach(
|
|
22
|
+
([key, group]) => {
|
|
23
|
+
this.manifest[key] = group.chunks.reduce(
|
|
24
|
+
(entry, chunk) => {
|
|
25
|
+
Array.from(chunk.files)
|
|
26
|
+
/**
|
|
27
|
+
* hot-update files will stomp on the main file, filter them out
|
|
28
|
+
*/
|
|
29
|
+
.filter((file) => !file.includes("hot-update"))
|
|
30
|
+
.forEach((file) => {
|
|
31
|
+
const { chunkGraph } = compilation;
|
|
32
|
+
const ext = path.extname(file);
|
|
33
|
+
|
|
34
|
+
const filepath = path.resolve(
|
|
35
|
+
compiler.options.output.publicPath,
|
|
36
|
+
file
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
let silo, fileKey;
|
|
40
|
+
/**
|
|
41
|
+
* If the chunk has one or more entryModules, it's a file
|
|
42
|
+
* If there are zero entryModules, it's a generated dependency
|
|
43
|
+
*/
|
|
44
|
+
if (chunkGraph.getNumberOfEntryModules(chunk) > 0) {
|
|
45
|
+
silo = "files";
|
|
46
|
+
fileKey = key + ext;
|
|
47
|
+
} else {
|
|
48
|
+
silo = "dependencies";
|
|
49
|
+
fileKey = chunk.id + ext;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
entry[silo][fileKey] = filepath;
|
|
53
|
+
});
|
|
54
|
+
return entry;
|
|
55
|
+
},
|
|
56
|
+
{ files: {}, dependencies: {} }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
callback();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
compiler.hooks.afterEmit.tapPromise(this.name, async () => {
|
|
65
|
+
if (this.config.writeManifestFile) {
|
|
66
|
+
return await fs.outputJSON(
|
|
67
|
+
path.resolve(compiler.options.output.path, this.config.manifestFile),
|
|
68
|
+
this.manifest,
|
|
69
|
+
{ spaces: 2 }
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
console.log("in DependencyManifestPlugin afterEmit");
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default DependencyManifestPlugin;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Container to provide plugin objects to the imageMin loader
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* We use the same svgo config for dev and prod
|
|
7
|
+
*/
|
|
8
|
+
const svgoConfig = {
|
|
9
|
+
js2svg: { pretty: true },
|
|
10
|
+
floatPrecision: 3,
|
|
11
|
+
plugins: [
|
|
12
|
+
{
|
|
13
|
+
name: "preset-default",
|
|
14
|
+
params: {
|
|
15
|
+
overrides: {
|
|
16
|
+
cleanupIDs: false,
|
|
17
|
+
removeViewBox: false,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
"sortAttrs"
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Optimized for size/quality
|
|
27
|
+
*/
|
|
28
|
+
const imageminProdPlugins = [
|
|
29
|
+
["gifsicle", { optimizationLevel: 3 }],
|
|
30
|
+
[
|
|
31
|
+
"pngquant",
|
|
32
|
+
{
|
|
33
|
+
strip: true,
|
|
34
|
+
dithering: 0.3,
|
|
35
|
+
quality: [0.5, 0.8],
|
|
36
|
+
verbose: true,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
["mozjpeg", { quality: 80, progressive: true }],
|
|
40
|
+
["svgo", svgoConfig],
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Optimized for speed
|
|
45
|
+
*/
|
|
46
|
+
const imageminDevPlugins = [
|
|
47
|
+
["gifsicle", { optimizationLevel: 1 }],
|
|
48
|
+
["optipng", { optimizationLevel: 0 }],
|
|
49
|
+
["jpegtran", { progressive: true }],
|
|
50
|
+
["svgo", svgoConfig],
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// const plugins = (isProd) => (isProd ? plugins.prod : plugins.dev);
|
|
54
|
+
// plugins.dev = imageminDevPlugins;
|
|
55
|
+
// plugins.prod = imageminProdPlugins;
|
|
56
|
+
// const plugins = (isProd) => (isProd ? imageminDevPlugins : imageminDevPlugins);
|
|
57
|
+
const plugins = (isProd) => (isProd ? imageminProdPlugins : imageminDevPlugins);
|
|
58
|
+
|
|
59
|
+
export default plugins;
|
|
60
|
+
// module.exports = plugins;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
/**
|
|
3
|
+
* Simple plugin for restoring the starting compile message which vanished in
|
|
4
|
+
* DevServer v4
|
|
5
|
+
*
|
|
6
|
+
* Messages should be configured in an argument object:
|
|
7
|
+
*
|
|
8
|
+
* new WatchRunReporterPlugin({message: "Your Message Here"})
|
|
9
|
+
*/
|
|
10
|
+
class WatchRunReporterPlugin {
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
const defaults = {
|
|
13
|
+
echo: true,
|
|
14
|
+
prefix: `🟢${chalk.gray("(iop)")}:`,
|
|
15
|
+
message: chalk.bold("Compiling..."),
|
|
16
|
+
};
|
|
17
|
+
this.config = { ...defaults, ...options };
|
|
18
|
+
this.name = "IOP Reporter Plugin";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
apply(compiler) {
|
|
22
|
+
compiler.hooks.watchRun.tapPromise(this.name, async () => {
|
|
23
|
+
if (this.config.echo) {
|
|
24
|
+
/**
|
|
25
|
+
* compiler.modifiedFiles is a Set containing the changed file or files
|
|
26
|
+
* along ith the base path containing those files (or the entryPoint?)
|
|
27
|
+
*
|
|
28
|
+
* Sorting the set (as an Array) puts the base path first (it's the shortest)
|
|
29
|
+
* then we remove it from the paths to clean up.
|
|
30
|
+
*
|
|
31
|
+
* Report a single changed path or the count of changed paths
|
|
32
|
+
*/
|
|
33
|
+
let msg = "";
|
|
34
|
+
if (compiler.modifiedFiles) {
|
|
35
|
+
let modFiles = Array.from(compiler.modifiedFiles).sort();
|
|
36
|
+
const basePath = modFiles[0];
|
|
37
|
+
const basePathPattern = new RegExp(`${basePath}/?`, "gi");
|
|
38
|
+
modFiles = modFiles
|
|
39
|
+
.map((p) => p.replace(basePathPattern, ""))
|
|
40
|
+
.filter((p) => p);
|
|
41
|
+
|
|
42
|
+
if (modFiles.length > 1) {
|
|
43
|
+
msg = `Modified ${modFiles.length - 1} files `;
|
|
44
|
+
} else {
|
|
45
|
+
msg = `Modified ${chalk.bold.yellow(modFiles[0] || basePath)} `;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
console.log(this.config.prefix, msg + this.config.message);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default WatchRunReporterPlugin;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import { posix as path } from "path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
|
|
5
|
+
// import defaultConfig from "../default.config.js";
|
|
6
|
+
|
|
7
|
+
const defaultConfig = {
|
|
8
|
+
src: "./src",
|
|
9
|
+
dist: "./dist",
|
|
10
|
+
entry: ["./js/index.js"],
|
|
11
|
+
publicPath: "/dist/",
|
|
12
|
+
contentBase: "/dist/", // TODO: Should this be false?
|
|
13
|
+
manifestFile: "./dependency-manifest.json",
|
|
14
|
+
sass: "sass-embedded",
|
|
15
|
+
port: 8080,
|
|
16
|
+
transpileDependencies: ["ansi-regex", "normalize-url"],
|
|
17
|
+
proxy: 'wordpress',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* This file encapsulates all the config file massaging and allows
|
|
26
|
+
* for asynchronous values.
|
|
27
|
+
*
|
|
28
|
+
* Asynchronous values:
|
|
29
|
+
* -
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
export default (configFile = { config: {} }) => {
|
|
33
|
+
/**
|
|
34
|
+
* This is a temporary workaround for a problem with using `process.env.NAME` in
|
|
35
|
+
* base ideasonpurpose.config.js files. `NAME` is often set in the environment
|
|
36
|
+
* and will override the value in package.json, resulting in orphan folders and
|
|
37
|
+
* misnamed archives.
|
|
38
|
+
*
|
|
39
|
+
* TODO: This warning can be removed once all process.env.NAME assignments are removed
|
|
40
|
+
* removed from project configuration files.
|
|
41
|
+
*/
|
|
42
|
+
if (configFile.filepath) {
|
|
43
|
+
const rawConfigFile = fs.readFileSync(configFile.filepath);
|
|
44
|
+
const packageJson = fs.readJsonSync(
|
|
45
|
+
path.resolve(configFile.filepath, "../package.json")
|
|
46
|
+
);
|
|
47
|
+
// console.log({ p: process.env.NAME });
|
|
48
|
+
if (
|
|
49
|
+
packageJson.name !== process.env.NAME &&
|
|
50
|
+
rawConfigFile.includes("process.env.NAME") &&
|
|
51
|
+
configFile.config.src.includes(process.env.NAME)
|
|
52
|
+
) {
|
|
53
|
+
nameEnvVarWarning();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Merge configFile onto defaults
|
|
59
|
+
*/
|
|
60
|
+
const config = { ...defaultConfig, ...configFile.config };
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Merge transpileDependency arrays
|
|
64
|
+
*/
|
|
65
|
+
if (configFile.config?.transpileDependencies) {
|
|
66
|
+
const configDeps =
|
|
67
|
+
typeof configFile.config.transpileDependencies === "string"
|
|
68
|
+
? [configFile.config.transpileDependencies]
|
|
69
|
+
: configFile.config.transpileDependencies;
|
|
70
|
+
config.transpileDependencies = [
|
|
71
|
+
...defaultConfig.transpileDependencies,
|
|
72
|
+
...configDeps,
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Normalize paths relative to the configFile
|
|
78
|
+
*
|
|
79
|
+
* When used with ideasonpurpose/docker-wordpress-dev the environment
|
|
80
|
+
* is set up like this:
|
|
81
|
+
*
|
|
82
|
+
* - Tools live in /usr/src/tools
|
|
83
|
+
* - Site is linked to /src/src/site
|
|
84
|
+
*
|
|
85
|
+
* placeholder.file is a sloppy workaround for needing a path ending in a file.
|
|
86
|
+
* This lets us move up a level in the same way we could if cosmiConfig returned
|
|
87
|
+
* the path to a config.js or package.json file.
|
|
88
|
+
*/
|
|
89
|
+
configFile.filepath = configFile.filepath || "../site/placeholder.file";
|
|
90
|
+
config.src = path.resolve(configFile.filepath, "..", config.src);
|
|
91
|
+
config.dist = path.resolve(configFile.filepath, "..", config.dist);
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check for `config.src` and create it if missing
|
|
95
|
+
* TODO: unresolved, create the file? Fail? Throw error?
|
|
96
|
+
*/
|
|
97
|
+
if (!fs.existsSync(config.src)) {
|
|
98
|
+
noSrcExists(config.src);
|
|
99
|
+
// TODO: Make this optional? User prompt? Throw error?
|
|
100
|
+
// TODO: This is a massive side-effect which makes testing very difficult
|
|
101
|
+
// fs.ensureDirSync(config.src);
|
|
102
|
+
// throw new Error();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Generate an entry object from config.entry.
|
|
107
|
+
* Output names will be based on the source file's basename.
|
|
108
|
+
*
|
|
109
|
+
* Config.entry should preferably be an array, but strings or objects will
|
|
110
|
+
* also work. Strings will be treated as a single-item array. Arrays will be
|
|
111
|
+
* parsed into an object, objects (or whatever) will pass through unmodified.
|
|
112
|
+
*
|
|
113
|
+
* A setting like this:
|
|
114
|
+
* [ "./js/index.js" ]
|
|
115
|
+
*
|
|
116
|
+
* Yields an entry object like this:
|
|
117
|
+
* { index: "./js/index.js"}
|
|
118
|
+
*
|
|
119
|
+
* A string will be wrapped in an array:
|
|
120
|
+
* "./js/main.js" => [ "./js/main.js" ]
|
|
121
|
+
*
|
|
122
|
+
* Objects pass straight through
|
|
123
|
+
* { app: "./js/main.js" }
|
|
124
|
+
*
|
|
125
|
+
* Overlapping basenames will be joined into a single entrypoint
|
|
126
|
+
* ["./index.js", "./app.js", "./index.scss"] => { app: ["./app.js"], index: ["./index.js", "./index.scss"]}
|
|
127
|
+
*/
|
|
128
|
+
if (typeof config.entry === "string") {
|
|
129
|
+
config.entry = [config.entry];
|
|
130
|
+
}
|
|
131
|
+
if (Array.isArray(config.entry)) {
|
|
132
|
+
config.entry = config.entry.reduce((obj, src) => {
|
|
133
|
+
obj[path.parse(src).name] = []
|
|
134
|
+
.concat(obj[path.parse(src).name], src)
|
|
135
|
+
.filter((i) => i);
|
|
136
|
+
return obj;
|
|
137
|
+
}, {});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Normalize the sass implementation, default to 'sass-embedded'
|
|
142
|
+
*
|
|
143
|
+
* Returns a known module name (string) which will be dynamically imported into the config
|
|
144
|
+
*/
|
|
145
|
+
const sassInput = config.sass.toString().toLowerCase();
|
|
146
|
+
const nodeSass = ["sass", "node-sass"];
|
|
147
|
+
config.sass = nodeSass.includes(sassInput) ? "sass" : "sass-embedded";
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Print a deprecation warning for node-sass
|
|
151
|
+
* @link https://sass-lang.com/blog/libsass-is-deprecated/
|
|
152
|
+
*/
|
|
153
|
+
if (sassInput === "node-sass") {
|
|
154
|
+
console.log(
|
|
155
|
+
"⚠️ ",
|
|
156
|
+
chalk.bold.yellow("NOTICE"),
|
|
157
|
+
chalk.yellow("NOTICE node-sass is no longer supported. The js-compiled")
|
|
158
|
+
);
|
|
159
|
+
console.log(chalk.yellow(" dart-sass package will be used instead."));
|
|
160
|
+
console.log(
|
|
161
|
+
chalk.cyan(" https://sass-lang.com/blog/libsass-is-deprecated/")
|
|
162
|
+
);
|
|
163
|
+
console.log(chalk.cyan(" https://www.npmjs.com/package/sass"));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Remap proxy: true to default value
|
|
168
|
+
*/
|
|
169
|
+
if (config.proxy === true) {
|
|
170
|
+
config.proxy = defaultConfig.proxy;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return config;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Warnings are broken out below
|
|
178
|
+
*/
|
|
179
|
+
const noSrcExists = (src) => {
|
|
180
|
+
console.log(
|
|
181
|
+
"🛑 ",
|
|
182
|
+
chalk.bold.red(
|
|
183
|
+
`ERROR: The src directory ${chalk.white(src)} does not exist.`
|
|
184
|
+
)
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const nameEnvVarWarning = () => {
|
|
189
|
+
console.log(
|
|
190
|
+
"⚠️ ",
|
|
191
|
+
chalk.yellow.bold(
|
|
192
|
+
"WARNING: $NAME is set in the environment and the project's config file."
|
|
193
|
+
)
|
|
194
|
+
);
|
|
195
|
+
console.log(
|
|
196
|
+
chalk.yellow(
|
|
197
|
+
" The value of $NAME will likely conflict with the name property from"
|
|
198
|
+
)
|
|
199
|
+
);
|
|
200
|
+
console.log(
|
|
201
|
+
chalk.yellow(
|
|
202
|
+
" package.json. Please update the project's config.js file. To silence "
|
|
203
|
+
)
|
|
204
|
+
);
|
|
205
|
+
console.log(
|
|
206
|
+
chalk.yellow(
|
|
207
|
+
" this message, set $NAME in the environment to match the project's"
|
|
208
|
+
)
|
|
209
|
+
);
|
|
210
|
+
console.log(chalk.yellow(" package.json `name` property."));
|
|
211
|
+
};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// const { dns } = require("dns").promises;
|
|
2
|
+
|
|
3
|
+
// DO NOT destructure this or we won't be able to mock it
|
|
4
|
+
// import {promises as dnsPromises} from "dns";
|
|
5
|
+
import dns from "dns";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { isIP } from "net";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* If config.proxy is explicitly not false, return a {proxy} object
|
|
11
|
+
* otherwise return an empty object.
|
|
12
|
+
*/
|
|
13
|
+
export default async (config) => {
|
|
14
|
+
let target;
|
|
15
|
+
|
|
16
|
+
console.log({config});
|
|
17
|
+
|
|
18
|
+
// if (
|
|
19
|
+
// config.proxy ===
|
|
20
|
+
// "http://devserver-proxy-token--d939bef2a41c4aa154ddb8db903ce19fff338b61"
|
|
21
|
+
// ) {
|
|
22
|
+
// /**
|
|
23
|
+
// * Deal with the legacy service hostname, re-map to 'wordpress' which will
|
|
24
|
+
// * be resolved to an IP address.
|
|
25
|
+
// *
|
|
26
|
+
// * TODO: Remove this warning after 2023-02 (extended another year, not hurting anything)
|
|
27
|
+
// */
|
|
28
|
+
|
|
29
|
+
// console.log(
|
|
30
|
+
// "⚠️ ",
|
|
31
|
+
// `The ${chalk.magenta(
|
|
32
|
+
// "devserver-proxy-token"
|
|
33
|
+
// )} value is no longer necessary`,
|
|
34
|
+
// "and can be safely removed from docker-compose"
|
|
35
|
+
// );
|
|
36
|
+
|
|
37
|
+
// /**
|
|
38
|
+
// * 'wordpress' is the internal hostname used by docker
|
|
39
|
+
// */
|
|
40
|
+
// target = await dns.promises.resolve("wordpress").catch(() => false);
|
|
41
|
+
// if (target) {
|
|
42
|
+
// target = "http://" + target.pop();
|
|
43
|
+
// }
|
|
44
|
+
// } else if (!!isIP(config.proxy)) {
|
|
45
|
+
if (!!isIP(config.proxy)) {
|
|
46
|
+
/**
|
|
47
|
+
* Bare IP addresses
|
|
48
|
+
* net.isIP() returns 4, 6 or 0(IPv4, IPv6 or not-an-IP address)
|
|
49
|
+
*/
|
|
50
|
+
target = `http://${config.proxy}`;
|
|
51
|
+
} else if (
|
|
52
|
+
typeof config.proxy == "string" &&
|
|
53
|
+
!config.proxy.match(/^https?:\/\//i)
|
|
54
|
+
) {
|
|
55
|
+
/**
|
|
56
|
+
* Handle plain strings that don't start with http:// or https://
|
|
57
|
+
* Attempt to resolve these to IP addresses
|
|
58
|
+
*/
|
|
59
|
+
target = await dns.promises.resolve(config.proxy).catch(() => false);
|
|
60
|
+
|
|
61
|
+
if (target) {
|
|
62
|
+
target = "http://" + target.pop();
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
/**
|
|
66
|
+
* Pass anything else straight through
|
|
67
|
+
*/
|
|
68
|
+
target = config.proxy;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Note: This is a sticky bit of code. Everything above gets bottlenecked here, and
|
|
73
|
+
* if aa previous value of target can't be converted to a URL, the whole proxy will
|
|
74
|
+
* return an empty object.
|
|
75
|
+
*/
|
|
76
|
+
try {
|
|
77
|
+
config.proxyUrl = new URL(target);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
// Invalid URL, unable to configure proxy.
|
|
80
|
+
return {};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// console.log({ target , proxyUrl: config.proxyUrl});
|
|
84
|
+
|
|
85
|
+
const proxy = {
|
|
86
|
+
"**": {
|
|
87
|
+
target,
|
|
88
|
+
secure: false,
|
|
89
|
+
autoRewrite: true,
|
|
90
|
+
selfHandleResponse: true, // necessary to avoid res.end being called automatically
|
|
91
|
+
changeOrigin: true, // needed for virtual hosted sites
|
|
92
|
+
cookieDomainRewrite: "", // was `${config.host}:8080` ??
|
|
93
|
+
headers: { "Accept-Encoding": "identity" },
|
|
94
|
+
|
|
95
|
+
onError: (err, req, res) => {
|
|
96
|
+
if (err.code === "ECONNRESET") {
|
|
97
|
+
console.log(chalk.yellow("Caught ECONNRESET error, ignoring..."));
|
|
98
|
+
} else {
|
|
99
|
+
console.log("PROXY ERROR: ", req.url, err, err.stack);
|
|
100
|
+
res.writeHead(500, { "Content-Type": "text-plain" });
|
|
101
|
+
res.end("Webpack DevServer Proxy Error: " + err);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
onProxyRes: function (proxyRes, req, res) {
|
|
106
|
+
/**
|
|
107
|
+
* Update urls in files with these content-types
|
|
108
|
+
*/
|
|
109
|
+
const contentTypes = [
|
|
110
|
+
"application/javascript",
|
|
111
|
+
"application/json",
|
|
112
|
+
"multipart/form-data",
|
|
113
|
+
"text/css",
|
|
114
|
+
"text/html",
|
|
115
|
+
"text/plain",
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
let originalBody = [];
|
|
119
|
+
|
|
120
|
+
proxyRes.on("data", (data) => originalBody.push(data));
|
|
121
|
+
|
|
122
|
+
proxyRes.on("end", () => {
|
|
123
|
+
res.statusCode = proxyRes.statusCode;
|
|
124
|
+
if (proxyRes.statusMessage) {
|
|
125
|
+
res.statusMessage = proxyRes.statusMessage;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
Object.keys(proxyRes.headers).forEach((key) => {
|
|
129
|
+
let header = proxyRes.headers[key];
|
|
130
|
+
if (typeof header == "string") {
|
|
131
|
+
header = header.replace(
|
|
132
|
+
new RegExp(config.proxyUrl.host, "gi"),
|
|
133
|
+
req.headers.host
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
res.setHeader(String(key).trim(), header);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
originalBody = Buffer.concat(originalBody);
|
|
140
|
+
let newBody;
|
|
141
|
+
const type = (proxyRes.headers["content-type"] || "").split(";")[0];
|
|
142
|
+
const wpRegexp = new RegExp(
|
|
143
|
+
"^/wp-(?:admin|includes|content/plugins).*(?:css|js|map)$"
|
|
144
|
+
);
|
|
145
|
+
const originRegExp = new RegExp(
|
|
146
|
+
`(http:\\\\/\\\\/|http://)${config.proxyUrl.host}`,
|
|
147
|
+
"gi"
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
if (contentTypes.includes(type) && !wpRegexp.test(req.path)) {
|
|
151
|
+
newBody = originalBody
|
|
152
|
+
.toString("utf8")
|
|
153
|
+
.replace(originRegExp, `$1${req.headers.host}`);
|
|
154
|
+
res.setHeader("Content-Length", Buffer.byteLength(newBody));
|
|
155
|
+
} else {
|
|
156
|
+
newBody = originalBody;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
res.end(newBody);
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
return { proxy };
|
|
166
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import prettyHrtime from "pretty-hrtime";
|
|
2
|
+
|
|
3
|
+
export const prettierHrtime = (hrtime) => {
|
|
4
|
+
let timeString;
|
|
5
|
+
const seconds = hrtime[1] > 5e6 ? " seconds" : " second";
|
|
6
|
+
if (hrtime[0] > 60) {
|
|
7
|
+
timeString = prettyHrtime(hrtime, { verbose: true })
|
|
8
|
+
.replace(/\d+ (milli|micro|nano)seconds/gi, "")
|
|
9
|
+
.trim();
|
|
10
|
+
} else if (hrtime[1] > 5e6) {
|
|
11
|
+
timeString = prettyHrtime(hrtime).replace(/ s$/, seconds);
|
|
12
|
+
} else {
|
|
13
|
+
timeString = prettyHrtime(hrtime);
|
|
14
|
+
}
|
|
15
|
+
return timeString;
|
|
16
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ideasonpurpose/build-tools-wordpress",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Build scripts and dependencies for IOP's WordPress development environments.",
|
|
5
|
+
"homepage": "https://github.com/ideasonpurpose/build-tools-wordpress#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/ideasonpurpose/build-tools-wordpress/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/ideasonpurpose/build-tools-wordpress.git"
|
|
12
|
+
},
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "Ideas On Purpose (https://www.ideasonpurpose.com/)",
|
|
15
|
+
"contributors": [
|
|
16
|
+
"Joe Maller <joe@ideasonpurpose.com>",
|
|
17
|
+
"Codrin Pavel <codrin@ideasonpurpose.com>"
|
|
18
|
+
],
|
|
19
|
+
"type": "module",
|
|
20
|
+
"main": "index.js",
|
|
21
|
+
"bin": {
|
|
22
|
+
"iop-build-port-reporter": "port-reporter.js"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"version": "version-everything && auto-changelog && git add -u"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@rollup/plugin-commonjs": "^25.0.7",
|
|
29
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
30
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
31
|
+
"@svgr/webpack": "^8.1.0",
|
|
32
|
+
"@wordpress/dependency-extraction-webpack-plugin": "^5.0.0",
|
|
33
|
+
"ansi-html": "^0.0.9",
|
|
34
|
+
"archiver": "^6.0.1",
|
|
35
|
+
"auto-changelog": "^2.4.0",
|
|
36
|
+
"autoprefixer": "^10.4.17",
|
|
37
|
+
"babel-loader": "^9.1.3",
|
|
38
|
+
"body-parser": "^1.20.2",
|
|
39
|
+
"caniuse-lite": "^1.0.30001579",
|
|
40
|
+
"chalk": "^5.3.0",
|
|
41
|
+
"cli-truncate": "^4.0.0",
|
|
42
|
+
"copy-webpack-plugin": "^12.0.2",
|
|
43
|
+
"cosmiconfig": "^9.0.0",
|
|
44
|
+
"css-loader": "^6.9.1",
|
|
45
|
+
"cssnano": "^6.0.1",
|
|
46
|
+
"del": "^7.1.0",
|
|
47
|
+
"dotenv": "^16.3.2",
|
|
48
|
+
"esbuild-loader": "^4.0.3",
|
|
49
|
+
"filesize": "^10.0.12",
|
|
50
|
+
"fs-extra": "^11.1.1",
|
|
51
|
+
"globby": "^14.0.0",
|
|
52
|
+
"http-proxy": "^1.18.1",
|
|
53
|
+
"humanize-duration": "^3.31.0",
|
|
54
|
+
"image-minimizer-webpack-plugin": "^4.0.0",
|
|
55
|
+
"is-text-path": "^2.0.0",
|
|
56
|
+
"lodash": "^4.17.21",
|
|
57
|
+
"mini-css-extract-plugin": "^2.7.6",
|
|
58
|
+
"node-sass": "^9.0.0",
|
|
59
|
+
"postcss": "^8.4.29",
|
|
60
|
+
"postcss-loader": "^8.0.0",
|
|
61
|
+
"pretty-hrtime": "^1.0.3",
|
|
62
|
+
"replacestream": "^4.0.3",
|
|
63
|
+
"sass": "^1.70.0",
|
|
64
|
+
"sass-embedded": "^1.70.0",
|
|
65
|
+
"sass-loader": "^14.0.0",
|
|
66
|
+
"sharp": "^0.33.2",
|
|
67
|
+
"source-map-explorer": "^2.5.3",
|
|
68
|
+
"string-length": "^6.0.0",
|
|
69
|
+
"style-loader": "^3.3.3",
|
|
70
|
+
"svgo": "^3.0.2",
|
|
71
|
+
"svgo-loader": "^4.0.0",
|
|
72
|
+
"version-everything": "^0.11.0",
|
|
73
|
+
"webpack": "^5.88.2",
|
|
74
|
+
"webpack-bundle-analyzer": "^4.9.1",
|
|
75
|
+
"webpack-cli": "^5.1.4",
|
|
76
|
+
"webpack-dev-middleware": "^7.0.0",
|
|
77
|
+
"webpack-dev-server": "^4.15.1"
|
|
78
|
+
},
|
|
79
|
+
"version-everything": {
|
|
80
|
+
"files": [
|
|
81
|
+
"README.md"
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
"directories": {
|
|
85
|
+
"lib": "lib"
|
|
86
|
+
},
|
|
87
|
+
"devDependencies": {}
|
|
88
|
+
}
|