@wp-playground/wordpress 3.0.46 → 3.0.51
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/boot.d.ts +13 -1
- package/index.cjs +2 -2
- package/index.js +13 -18
- package/index.js.map +1 -1
- package/package.json +10 -8
package/boot.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CookieStore, FileNotFoundAction, FileNotFoundGetActionCallback, FileTree, PHPWorker, SpawnHandler, Remote } from '@php-wasm/universal';
|
|
1
|
+
import type { CookieStore, FileNotFoundAction, FileNotFoundGetActionCallback, FileTree, PathAlias, PHPWorker, SpawnHandler, Remote } from '@php-wasm/universal';
|
|
2
2
|
import { PHP, PHPRequestHandler } from '@php-wasm/universal';
|
|
3
3
|
export type PhpIniOptions = Record<string, string>;
|
|
4
4
|
export type Hook = (php: PHP) => void | Promise<void>;
|
|
@@ -62,6 +62,18 @@ export interface BootRequestHandlerOptions {
|
|
|
62
62
|
* given request URI.
|
|
63
63
|
*/
|
|
64
64
|
getFileNotFoundAction?: FileNotFoundGetActionCallback;
|
|
65
|
+
/**
|
|
66
|
+
* Path aliases that map URL prefixes to filesystem paths outside
|
|
67
|
+
* the document root. Similar to Nginx's `alias` directive.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* pathAliases: [
|
|
72
|
+
* { urlPrefix: '/phpmyadmin', fsPath: '/tools/phpmyadmin' }
|
|
73
|
+
* ]
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
pathAliases?: PathAlias[];
|
|
65
77
|
/**
|
|
66
78
|
* The CookieStore instance to use.
|
|
67
79
|
*
|
package/index.cjs
CHANGED
|
@@ -352,7 +352,7 @@ function skip_whitespace($tokens) {
|
|
|
352
352
|
ob_clean();
|
|
353
353
|
echo false === $return_value ? '0' : '1';
|
|
354
354
|
ob_end_flush();
|
|
355
|
-
`})).text!=="1")throw new Error("Failed to rewrite constants in wp-config.php.")}async function $(e,n){const t=a.joinPaths(n,"wp-config.php"),i={DB_NAME:"wordpress"};!e.fileExists(t)&&e.fileExists(a.joinPaths(n,"wp-config-sample.php"))&&await e.writeFile(t,await e.readFileAsBuffer(a.joinPaths(n,"wp-config-sample.php"))),await g(e,t,i,"skip")}async function I(e){const n=await m(e);return await w(n,e),n}async function w(e,n){var d,s;const t=await e.getPrimaryPhp();if((d=n.hooks)!=null&&d.beforeWordPressFiles&&await n.hooks.beforeWordPressFiles(t),n.wordPressZip&&await S(t,await n.wordPressZip),n.constants)for(const l in n.constants)t.defineConstant(l,n.constants[l]);n.dataSqlPath&&(t.defineConstant("DB_DIR",a.dirname(n.dataSqlPath)),t.defineConstant("DB_FILE",a.basename(n.dataSqlPath))),t.defineConstant("WP_HOME",n.siteUrl),t.defineConstant("WP_SITEURL",n.siteUrl),await $(t,e.documentRoot),(s=n.hooks)!=null&&s.beforeDatabaseSetup&&await n.hooks.beforeDatabaseSetup(t);let i=!1;n.sqliteIntegrationPluginZip&&(i=!0,await T(t,await n.sqliteIntegrationPluginZip));const r=n.wordpressInstallMode??"download-and-install",o=!!n.dataSqlPath;if(["download-and-install","install-from-existing-files"].includes(r)){await f(e,{usesSqlite:i,hasCustomDatabasePath:o});try{await _(t)}catch(l){throw o||await p(e),l}o||await p(e)}else if(r==="install-from-existing-files-if-needed"){if(await f(e,{usesSqlite:i,hasCustomDatabasePath:o}),!await b(t))try{await _(t)}catch(l){throw o||await p(e),l}o||await p(e)}return e}async function f(e,{usesSqlite:n,hasCustomDatabasePath:t}){const i=await e.getPrimaryPhp();if(i.isFile("/internal/shared/preload/0-sqlite.php"))return;const r=a.joinPaths(e.documentRoot,"wp-content/mu-plugins/sqlite-database-integration");if(!i.isDir(r)&&!n&&!t)throw new Error("Error connecting to the MySQL database.")}async function p(e){const n=await e.getPrimaryPhp();if(await L(n))return;if(n.isFile("/internal/shared/preload/0-sqlite.php"))throw new Error("Error connecting to the SQLite database.");const i=a.joinPaths(e.documentRoot,"wp-content/mu-plugins/sqlite-database-integration");throw n.isDir(i)?new Error("Error connecting to the SQLite database."):new Error("Error connecting to the MySQL database.")}async function m(e){const n=e.spawnHandler??u.sandboxedSpawnHandlerFactory;async function t(r,o=!1){const d=await e.createPhpRuntime(o),s=new u.PHP(d);if(e.sapiName&&s.setSapiName(e.sapiName),r&&(s.requestHandler=r),e.phpIniEntries&&u.setPhpIniEntries(s,e.phpIniEntries),s.defineConstant("WP_SQLITE_AST_DRIVER",!0),e.constants)for(const l in e.constants)s.defineConstant(l,e.constants[l]);return o&&!s.isFile("/internal/.boot-files-written")&&(await E(s),await u.writeFiles(s,"/",e.createFiles||{}),await R(s,a.joinPaths(new URL(e.siteUrl).pathname,"phpinfo.php")),await u.writeFiles(s,"/internal",{".boot-files-written":""})),n&&await s.setSpawnHandler(n(r?()=>r.instanceManager.acquirePHPInstance(
|
|
355
|
+
`})).text!=="1")throw new Error("Failed to rewrite constants in wp-config.php.")}async function $(e,n){const t=a.joinPaths(n,"wp-config.php"),i={DB_NAME:"wordpress"};!e.fileExists(t)&&e.fileExists(a.joinPaths(n,"wp-config-sample.php"))&&await e.writeFile(t,await e.readFileAsBuffer(a.joinPaths(n,"wp-config-sample.php"))),await g(e,t,i,"skip")}async function I(e){const n=await m(e);return await w(n,e),n}async function w(e,n){var d,s;const t=await e.getPrimaryPhp();if((d=n.hooks)!=null&&d.beforeWordPressFiles&&await n.hooks.beforeWordPressFiles(t),n.wordPressZip&&await S(t,await n.wordPressZip),n.constants)for(const l in n.constants)t.defineConstant(l,n.constants[l]);n.dataSqlPath&&(t.defineConstant("DB_DIR",a.dirname(n.dataSqlPath)),t.defineConstant("DB_FILE",a.basename(n.dataSqlPath))),t.defineConstant("WP_HOME",n.siteUrl),t.defineConstant("WP_SITEURL",n.siteUrl),await $(t,e.documentRoot),(s=n.hooks)!=null&&s.beforeDatabaseSetup&&await n.hooks.beforeDatabaseSetup(t);let i=!1;n.sqliteIntegrationPluginZip&&(i=!0,await T(t,await n.sqliteIntegrationPluginZip));const r=n.wordpressInstallMode??"download-and-install",o=!!n.dataSqlPath;if(["download-and-install","install-from-existing-files"].includes(r)){await f(e,{usesSqlite:i,hasCustomDatabasePath:o});try{await _(t)}catch(l){throw o||await p(e),l}o||await p(e)}else if(r==="install-from-existing-files-if-needed"){if(await f(e,{usesSqlite:i,hasCustomDatabasePath:o}),!await b(t))try{await _(t)}catch(l){throw o||await p(e),l}o||await p(e)}return e}async function f(e,{usesSqlite:n,hasCustomDatabasePath:t}){const i=await e.getPrimaryPhp();if(i.isFile("/internal/shared/preload/0-sqlite.php"))return;const r=a.joinPaths(e.documentRoot,"wp-content/mu-plugins/sqlite-database-integration");if(!i.isDir(r)&&!n&&!t)throw new Error("Error connecting to the MySQL database.")}async function p(e){const n=await e.getPrimaryPhp();if(await L(n))return;if(n.isFile("/internal/shared/preload/0-sqlite.php"))throw new Error("Error connecting to the SQLite database.");const i=a.joinPaths(e.documentRoot,"wp-content/mu-plugins/sqlite-database-integration");throw n.isDir(i)?new Error("Error connecting to the SQLite database."):new Error("Error connecting to the MySQL database.")}async function m(e){const n=e.spawnHandler??u.sandboxedSpawnHandlerFactory;async function t(r,o=!1){const d=await e.createPhpRuntime(o),s=new u.PHP(d);if(e.sapiName&&s.setSapiName(e.sapiName),r&&(s.requestHandler=r),e.phpIniEntries&&u.setPhpIniEntries(s,e.phpIniEntries),s.defineConstant("WP_SQLITE_AST_DRIVER",!0),e.constants)for(const l in e.constants)s.defineConstant(l,e.constants[l]);return o&&!s.isFile("/internal/.boot-files-written")&&(await E(s),await u.writeFiles(s,"/",e.createFiles||{}),await R(s,a.joinPaths(new URL(e.siteUrl).pathname,"phpinfo.php")),await u.writeFiles(s,"/internal",{".boot-files-written":""})),n&&await s.setSpawnHandler(n(r?()=>r.instanceManager.acquirePHPInstance():void 0)),s.enableRuntimeRotation({recreateRuntime:e.createPhpRuntime,maxRequests:400}),e.onPHPInstanceCreated&&await e.onPHPInstanceCreated(s,{isPrimary:o}),s}const i=new u.PHPRequestHandler({documentRoot:e.documentRoot||"/wordpress",absoluteUrl:e.siteUrl,rewriteRules:k,pathAliases:e.pathAliases,getFileNotFoundAction:e.getFileNotFoundAction??P,cookieStore:e.cookieStore,php:e.maxPhpInstances===1?await t(void 0,!0):void 0,phpFactory:e.maxPhpInstances!==1?async({isPrimary:r})=>t(i,r):void 0,maxPhpInstances:e.maxPhpInstances});return i}async function b(e){return(await e.run({code:`<?php
|
|
356
356
|
ob_start();
|
|
357
357
|
$wp_load = getenv('DOCUMENT_ROOT') . '/wp-load.php';
|
|
358
358
|
if (!file_exists($wp_load)) {
|
|
@@ -394,7 +394,7 @@ function skip_whitespace($tokens) {
|
|
|
394
394
|
ob_clean();
|
|
395
395
|
echo $wpdb->check_connection( false ) ? '1' : '0';
|
|
396
396
|
ob_end_flush();
|
|
397
|
-
`,env:{DOCUMENT_ROOT:e.documentRoot}})).text==="1"}async function W(e){const{php:n,reap:t}=await e.instanceManager.acquirePHPInstance(
|
|
397
|
+
`,env:{DOCUMENT_ROOT:e.documentRoot}})).text==="1"}async function W(e){const{php:n,reap:t}=await e.instanceManager.acquirePHPInstance();try{const r=(await n.run({code:`<?php
|
|
398
398
|
require '${e.documentRoot}/wp-includes/version.php';
|
|
399
399
|
echo $wp_version;
|
|
400
400
|
`})).text;if(!r)throw new Error("Unable to read loaded WordPress version.");return y(r)}finally{t()}}function y(e){if(/-(alpha|beta|RC)\d*-\d+$/.test(e))return"trunk";if(/-(beta|RC)\d*$/.test(e))return"beta";const i=e.match(/^(\d+\.\d+)(?:\.\d+)?$/);return i!==null?i[1]:e}const k=[{match:new RegExp("^(/[_0-9a-zA-Z-]+)?(/wp-(content|admin|includes)/.*)"),replacement:"$2"}];async function E(e){await e.mkdir("/internal/shared/mu-plugins"),await e.writeFile("/internal/shared/preload/env.php",`<?php
|
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { phpVars as m, joinPaths as o, dirname as b, basename as y, phpVar as u } from "@php-wasm/util";
|
|
2
|
-
import { createMemoizedFetch as P, unzipFile as
|
|
2
|
+
import { createMemoizedFetch as P, unzipFile as f } from "@wp-playground/common";
|
|
3
3
|
import { logger as g } from "@php-wasm/logger";
|
|
4
|
-
import { sandboxedSpawnHandlerFactory as k, PHPRequestHandler as E,
|
|
4
|
+
import { sandboxedSpawnHandlerFactory as k, PHPRequestHandler as E, PHP as R, setPhpIniEntries as T, writeFiles as c, withPHPIniValues as S } from "@php-wasm/universal";
|
|
5
5
|
const x = `<?php
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -448,8 +448,8 @@ async function p(e) {
|
|
|
448
448
|
async function U(e) {
|
|
449
449
|
const n = e.spawnHandler ?? k;
|
|
450
450
|
async function t(r, s = !1) {
|
|
451
|
-
const d = await e.createPhpRuntime(s), a = new
|
|
452
|
-
if (e.sapiName && a.setSapiName(e.sapiName), r && (a.requestHandler = r), e.phpIniEntries &&
|
|
451
|
+
const d = await e.createPhpRuntime(s), a = new R(d);
|
|
452
|
+
if (e.sapiName && a.setSapiName(e.sapiName), r && (a.requestHandler = r), e.phpIniEntries && T(a, e.phpIniEntries), a.defineConstant("WP_SQLITE_AST_DRIVER", !0), e.constants)
|
|
453
453
|
for (const l in e.constants)
|
|
454
454
|
a.defineConstant(l, e.constants[l]);
|
|
455
455
|
return s && /**
|
|
@@ -462,18 +462,14 @@ async function U(e) {
|
|
|
462
462
|
* mechanism. It works, because secondary workers are only booted
|
|
463
463
|
* once the primary worker has fully booted.
|
|
464
464
|
*/
|
|
465
|
-
!a.isFile("/internal/.boot-files-written") && (await A(a), await
|
|
465
|
+
!a.isFile("/internal/.boot-files-written") && (await A(a), await c(a, "/", e.createFiles || {}), await q(
|
|
466
466
|
a,
|
|
467
467
|
o(new URL(e.siteUrl).pathname, "phpinfo.php")
|
|
468
|
-
), await
|
|
468
|
+
), await c(a, "/internal", {
|
|
469
469
|
".boot-files-written": ""
|
|
470
470
|
})), n && await a.setSpawnHandler(
|
|
471
471
|
n(
|
|
472
|
-
r ? () => r.instanceManager.acquirePHPInstance(
|
|
473
|
-
{
|
|
474
|
-
considerPrimary: !1
|
|
475
|
-
}
|
|
476
|
-
) : void 0
|
|
472
|
+
r ? () => r.instanceManager.acquirePHPInstance() : void 0
|
|
477
473
|
)
|
|
478
474
|
), a.enableRuntimeRotation({
|
|
479
475
|
recreateRuntime: e.createPhpRuntime,
|
|
@@ -484,6 +480,7 @@ async function U(e) {
|
|
|
484
480
|
documentRoot: e.documentRoot || "/wordpress",
|
|
485
481
|
absoluteUrl: e.siteUrl,
|
|
486
482
|
rewriteRules: D,
|
|
483
|
+
pathAliases: e.pathAliases,
|
|
487
484
|
getFileNotFoundAction: e.getFileNotFoundAction ?? W,
|
|
488
485
|
cookieStore: e.cookieStore,
|
|
489
486
|
/**
|
|
@@ -495,7 +492,7 @@ async function U(e) {
|
|
|
495
492
|
/**
|
|
496
493
|
* If maxPhpInstances is not 1, the PHPRequestHandler constructor needs
|
|
497
494
|
* a PHP factory function. Internally, it creates a PHPProcessManager that
|
|
498
|
-
*
|
|
495
|
+
* maintains a pool of reusable PHP instances.
|
|
499
496
|
*/
|
|
500
497
|
phpFactory: e.maxPhpInstances !== 1 ? async ({ isPrimary: r }) => t(i, r) : void 0,
|
|
501
498
|
maxPhpInstances: e.maxPhpInstances
|
|
@@ -523,7 +520,7 @@ async function $(e) {
|
|
|
523
520
|
}
|
|
524
521
|
async function h(e) {
|
|
525
522
|
var i;
|
|
526
|
-
const n = await
|
|
523
|
+
const n = await S(
|
|
527
524
|
e,
|
|
528
525
|
{
|
|
529
526
|
disable_functions: "fsockopen",
|
|
@@ -606,9 +603,7 @@ async function C(e) {
|
|
|
606
603
|
})).text === "1";
|
|
607
604
|
}
|
|
608
605
|
async function j(e) {
|
|
609
|
-
const { php: n, reap: t } = await e.instanceManager.acquirePHPInstance(
|
|
610
|
-
considerPrimary: !0
|
|
611
|
-
});
|
|
606
|
+
const { php: n, reap: t } = await e.instanceManager.acquirePHPInstance();
|
|
612
607
|
try {
|
|
613
608
|
const r = (await n.run({
|
|
614
609
|
code: `<?php
|
|
@@ -977,7 +972,7 @@ async function q(e, n = "/phpinfo.php") {
|
|
|
977
972
|
async function F(e, n) {
|
|
978
973
|
await e.isDir("/tmp/sqlite-database-integration") && await e.rmdir("/tmp/sqlite-database-integration", {
|
|
979
974
|
recursive: !0
|
|
980
|
-
}), await e.mkdir("/tmp/sqlite-database-integration"), await
|
|
975
|
+
}), await e.mkdir("/tmp/sqlite-database-integration"), await f(e, n, "/tmp/sqlite-database-integration");
|
|
981
976
|
const t = "/internal/shared/sqlite-database-integration", i = `/tmp/sqlite-database-integration/${(await e.listFiles("/tmp/sqlite-database-integration"))[0]}`;
|
|
982
977
|
await e.mv(i, t), await e.defineConstant("SQLITE_MAIN_FILE", "1");
|
|
983
978
|
const s = (await e.readFileAsText(
|
|
@@ -1084,7 +1079,7 @@ if(!function_exists('mysqli_connect')) {
|
|
|
1084
1079
|
);
|
|
1085
1080
|
}
|
|
1086
1081
|
async function N(e, n) {
|
|
1087
|
-
e.mkdir("/tmp/unzipped-wordpress"), await
|
|
1082
|
+
e.mkdir("/tmp/unzipped-wordpress"), await f(e, n, "/tmp/unzipped-wordpress"), e.fileExists("/tmp/unzipped-wordpress/wordpress.zip") && await f(
|
|
1088
1083
|
e,
|
|
1089
1084
|
"/tmp/unzipped-wordpress/wordpress.zip",
|
|
1090
1085
|
"/tmp/unzipped-wordpress"
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wp-playground/wordpress",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.51",
|
|
4
4
|
"description": "WordPress-related plumbing for WordPress Playground",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -35,18 +35,18 @@
|
|
|
35
35
|
"access": "public",
|
|
36
36
|
"directory": "../../../dist/packages/playground/wordpress"
|
|
37
37
|
},
|
|
38
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "ebc05da827807f3311ed1de8711fa74266183fab",
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"express": "4.22.0",
|
|
41
41
|
"ini": "4.1.2",
|
|
42
42
|
"wasm-feature-detect": "1.8.0",
|
|
43
43
|
"ws": "8.18.3",
|
|
44
44
|
"yargs": "17.7.2",
|
|
45
|
-
"@php-wasm/universal": "3.0.
|
|
46
|
-
"@php-wasm/util": "3.0.
|
|
47
|
-
"@php-wasm/logger": "3.0.
|
|
48
|
-
"@wp-playground/common": "3.0.
|
|
49
|
-
"@php-wasm/node": "3.0.
|
|
45
|
+
"@php-wasm/universal": "3.0.51",
|
|
46
|
+
"@php-wasm/util": "3.0.51",
|
|
47
|
+
"@php-wasm/logger": "3.0.51",
|
|
48
|
+
"@wp-playground/common": "3.0.51",
|
|
49
|
+
"@php-wasm/node": "3.0.51"
|
|
50
50
|
},
|
|
51
51
|
"packageManager": "npm@10.9.2",
|
|
52
52
|
"overrides": {
|
|
@@ -57,7 +57,9 @@
|
|
|
57
57
|
"@playwright/test": "1.55.1",
|
|
58
58
|
"ws": "8.18.3",
|
|
59
59
|
"tmp": "0.2.5",
|
|
60
|
-
"form-data": "^4.0.4"
|
|
60
|
+
"form-data": "^4.0.4",
|
|
61
|
+
"lodash": "^4.17.23",
|
|
62
|
+
"glob": "^9.3.0"
|
|
61
63
|
},
|
|
62
64
|
"optionalDependencies": {
|
|
63
65
|
"fs-ext": "2.1.1"
|