diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0ff9c84..f6b4503 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -28,5 +28,8 @@ jobs:
- name: lint
run: pnpm lint
- - name: build
- run: pnpm build
+ - name: build && test
+ run: |
+ npm install serve cypress -g
+ pnpm add cypress -D && cypress install
+ pnpm test:ci
diff --git a/.gitignore b/.gitignore
index 4a7f73a..191e6a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,6 @@ logs
.env
.env.*
!.env.example
+
+# Cypress specific
+cypress/screenshots
diff --git a/app/components-private/Footer.vue b/app/components-private/Footer.vue
index 4f57a43..1612508 100644
--- a/app/components-private/Footer.vue
+++ b/app/components-private/Footer.vue
@@ -6,15 +6,15 @@
-
- 确定
+ 确定
diff --git a/cypress/cypress.config.ts b/cypress/cypress.config.ts
new file mode 100644
index 0000000..e0103a8
--- /dev/null
+++ b/cypress/cypress.config.ts
@@ -0,0 +1,10 @@
+import { defineConfig } from 'cypress';
+
+export default defineConfig({
+ e2e: {
+ baseUrl: 'http://localhost:3000',
+ chromeWebSecurity: false,
+ specPattern: 'cypress/e2e/**/*.spec.*',
+ supportFile: false,
+ },
+});
diff --git a/cypress/e2e/index.spec.ts b/cypress/e2e/index.spec.ts
new file mode 100644
index 0000000..77e45a4
--- /dev/null
+++ b/cypress/e2e/index.spec.ts
@@ -0,0 +1,105 @@
+context('首页', () => {
+ beforeEach(() => {
+ cy.visit('/');
+ cy.contains('示例页').should('exist');
+ });
+
+ it('跳转测试', () => {
+ cy.url()
+ .should('eq', 'http://localhost:3000/');
+
+ cy.contains('基础项目模板').should('exist');
+
+ cy.get('#name-input')
+ .type('mixte{enter}')
+ .url()
+ .should('eq', 'http://localhost:3000/hello/mixte');
+
+ cy.get('#back')
+ .click()
+ .url()
+ .should('eq', 'http://localhost:3000/');
+
+ cy.get('#go')
+ .should('be.disabled');
+
+ cy.get('#name-input')
+ .type('Zw');
+
+ cy.get('#go')
+ .should('not.be.disabled')
+ .click()
+ .url()
+ .should('eq', 'http://localhost:3000/hello/Zw666');
+ });
+
+ it('底部按钮 - [首页]', () => {
+ cy.url()
+ .should('eq', 'http://localhost:3000/');
+
+ cy.get('button[title="首页"]')
+ .click()
+ .url()
+ .should('eq', 'http://localhost:3000/');
+
+ cy.get('#name-input')
+ .type('mixte{enter}');
+
+ cy.url()
+ .should('eq', 'http://localhost:3000/hello/mixte');
+
+ cy.get('button[title="首页"]')
+ .click()
+ .url()
+ .should('eq', 'http://localhost:3000/');
+ });
+
+ it('底部按钮 - [切换深色模式]', () => {
+ const btnSelector = 'button[title="切换深色模式"]';
+
+ Cypress.$.each(['one', 'two'], (index) => {
+ // light
+
+ cy.get(`${btnSelector}[data-theme="light"]`)
+ .should('exist');
+
+ cy.get('html')
+ .should('have.class', 'light')
+ .should('not.have.class', 'dark');
+
+ cy.get(btnSelector).click();
+
+ // dark
+
+ cy.get(`${btnSelector}[data-theme="dark"]`)
+ .should('exist');
+
+ cy.get('html')
+ .should('have.class', 'dark')
+ .should('not.have.class', 'light');
+
+ cy.get(btnSelector).click();
+
+ // system ( light )
+
+ cy.get(`${btnSelector}[data-theme="system"]`)
+ .should('exist');
+
+ cy.get('html')
+ .should('have.class', 'light')
+ .should('not.have.class', 'dark');
+
+ // reset
+ if (!index) {
+ cy.get(btnSelector).click();
+ }
+ });
+ });
+
+ it('底部按钮 - [跳转到 Github]', () => {
+ cy.get('button[title="跳转到 Github"]')
+ .children('a')
+ .should('have.attr', 'target', '_blank')
+ .should('have.attr', 'href', 'https://github.com/MoomFE-Starter-Template/Base');
+ });
+});
diff --git a/cypress/scripts/run-test.ts b/cypress/scripts/run-test.ts
new file mode 100644
index 0000000..d5e32ad
--- /dev/null
+++ b/cypress/scripts/run-test.ts
@@ -0,0 +1,30 @@
+import { spawn } from 'node:child_process';
+import process from 'node:process';
+
+const argv = process.argv.slice(2);
+const command = argv.includes('--ci') ? 'run' : 'open';
+
+// 启动预览
+const preview = spawn('npm', ['run', 'preview'], {
+ shell: true,
+ stdio: 'inherit',
+});
+
+// 等待预览启动
+const wait = spawn('wait-on', ['http://localhost:3000'], {
+ shell: true,
+});
+
+// 等待预览启动完成后
+wait.on('exit', () => {
+ // 启动 Cypress 测试
+ const cypress = spawn('cypress', [command, '--config-file', 'cypress/cypress.config.ts'], {
+ shell: true,
+ stdio: 'inherit',
+ });
+
+ // 当 Cypress 进程退出时,关闭预览进程
+ cypress.on('exit', () => {
+ preview.kill();
+ });
+});
diff --git a/package.json b/package.json
index 21a896a..86a3bae 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,8 @@
"preview:ssr": "pnpm build:ssr && nuxt preview --host",
"lint": "pnpm i && eslint .",
"lint:fix": "pnpm i && eslint . --fix",
+ "test": "tsx ./cypress/scripts/run-test.ts",
+ "test:ci": "tsx ./cypress/scripts/run-test.ts --ci",
"up": "npx taze latest -w -i",
"postinstall": "nuxt prepare"
},
@@ -43,11 +45,13 @@
"pathe": "^1.1.2",
"pnpm": "^9.7.1",
"sass": "^1.77.8",
+ "tsx": "^4.17.0",
"type-fest": "^4.24.0",
"typescript": "^5.5.4",
"unocss": "^0.62.1",
"unocss-preset-extra": "^0.5.3",
"unplugin-icons": "^0.19.2",
- "unplugin-vue-components": "^0.27.4"
+ "unplugin-vue-components": "^0.27.4",
+ "wait-on": "^7.2.0"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8807246..31d8937 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -81,6 +81,9 @@ importers:
sass:
specifier: ^1.77.8
version: 1.77.8
+ tsx:
+ specifier: ^4.17.0
+ version: 4.17.0
type-fest:
specifier: ^4.24.0
version: 4.24.0
@@ -99,6 +102,9 @@ importers:
unplugin-vue-components:
specifier: ^0.27.4
version: 0.27.4(@babel/parser@7.25.3)(@nuxt/kit@3.12.4(magicast@0.3.4)(rollup@4.19.0))(rollup@4.19.0)(vue@3.4.37(typescript@5.5.4))
+ wait-on:
+ specifier: ^7.2.0
+ version: 7.2.0
packages:
@@ -878,6 +884,12 @@ packages:
'@floating-ui/utils@0.2.5':
resolution: {integrity: sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ==}
+ '@hapi/hoek@9.3.0':
+ resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
+
+ '@hapi/topo@5.1.0':
+ resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==}
+
'@humanwhocodes/module-importer@1.0.1':
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
engines: {node: '>=12.22'}
@@ -1298,6 +1310,15 @@ packages:
cpu: [x64]
os: [win32]
+ '@sideway/address@4.1.5':
+ resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==}
+
+ '@sideway/formula@3.0.1':
+ resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==}
+
+ '@sideway/pinpoint@2.0.0':
+ resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==}
+
'@sindresorhus/merge-streams@2.3.0':
resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==}
engines: {node: '>=18'}
@@ -1862,6 +1883,9 @@ packages:
async@3.2.5:
resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==}
+ asynckit@0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
autoprefixer@10.4.19:
resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==}
engines: {node: ^10 || ^12 || >=14}
@@ -1869,6 +1893,9 @@ packages:
peerDependencies:
postcss: ^8.1.0
+ axios@1.7.4:
+ resolution: {integrity: sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==}
+
b4a@1.6.6:
resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==}
@@ -2044,6 +2071,10 @@ packages:
colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
+ combined-stream@1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+
commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
@@ -2249,6 +2280,10 @@ packages:
defu@6.1.4:
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+ delayed-stream@1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+
delegates@1.0.0:
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
@@ -2706,10 +2741,23 @@ packages:
flatted@3.3.1:
resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
+ follow-redirects@1.15.6:
+ resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+
foreground-child@3.2.1:
resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==}
engines: {node: '>=14'}
+ form-data@4.0.0:
+ resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
+ engines: {node: '>= 6'}
+
fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
@@ -3074,6 +3122,9 @@ packages:
resolution: {integrity: sha512-c+PHQZakiQuMKbnhvrjZUvrK6E/AfmTOf4P+E3Y4FNVHcNMX9e/XrnbEvO+m4wS6ZjsvhHh/POQTlfy8uXFc0A==}
hasBin: true
+ joi@17.13.3:
+ resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==}
+
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@@ -3319,6 +3370,9 @@ packages:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
minipass@3.3.6:
resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
engines: {node: '>=8'}
@@ -3850,6 +3904,9 @@ packages:
protocols@2.0.1:
resolution: {integrity: sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==}
+ proxy-from-env@1.1.0:
+ resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@@ -3976,6 +4033,9 @@ packages:
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+ rxjs@7.8.1:
+ resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
+
safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
@@ -4306,8 +4366,8 @@ packages:
tslib@2.6.3:
resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
- tsx@4.16.2:
- resolution: {integrity: sha512-C1uWweJDgdtX2x600HjaFaucXTilT7tgUZHbOE4+ypskZ1OP8CRCSDkCxG6Vya9EwaFIVagWwpaVAn5wzypaqQ==}
+ tsx@4.17.0:
+ resolution: {integrity: sha512-eN4mnDA5UMKDt4YZixo9tBioibaMBpoxBkD+rIPAjVmYERSG0/dWEY1CEFuV89CgASlKL499q8AhmkMnnjtOJg==}
engines: {node: '>=18.0.0'}
hasBin: true
@@ -4696,6 +4756,11 @@ packages:
typescript:
optional: true
+ wait-on@7.2.0:
+ resolution: {integrity: sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==}
+ engines: {node: '>=12.0.0'}
+ hasBin: true
+
watchpack@2.4.1:
resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==}
engines: {node: '>=10.13.0'}
@@ -5547,6 +5612,12 @@ snapshots:
'@floating-ui/utils@0.2.5': {}
+ '@hapi/hoek@9.3.0': {}
+
+ '@hapi/topo@5.1.0':
+ dependencies:
+ '@hapi/hoek': 9.3.0
+
'@humanwhocodes/module-importer@1.0.1': {}
'@humanwhocodes/retry@0.3.0': {}
@@ -6165,6 +6236,14 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.19.0':
optional: true
+ '@sideway/address@4.1.5':
+ dependencies:
+ '@hapi/hoek': 9.3.0
+
+ '@sideway/formula@3.0.1': {}
+
+ '@sideway/pinpoint@2.0.0': {}
+
'@sindresorhus/merge-streams@2.3.0': {}
'@stylistic/eslint-plugin-js@2.6.1(eslint@9.9.0(jiti@1.21.6))':
@@ -7066,6 +7145,8 @@ snapshots:
async@3.2.5: {}
+ asynckit@0.4.0: {}
+
autoprefixer@10.4.19(postcss@8.4.39):
dependencies:
browserslist: 4.23.2
@@ -7076,6 +7157,14 @@ snapshots:
postcss: 8.4.39
postcss-value-parser: 4.2.0
+ axios@1.7.4:
+ dependencies:
+ follow-redirects: 1.15.6
+ form-data: 4.0.0
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+
b4a@1.6.6: {}
balanced-match@1.0.2: {}
@@ -7248,6 +7337,10 @@ snapshots:
colorette@2.0.20: {}
+ combined-stream@1.0.8:
+ dependencies:
+ delayed-stream: 1.0.0
+
commander@2.20.3: {}
commander@7.2.0: {}
@@ -7420,6 +7513,8 @@ snapshots:
defu@6.1.4: {}
+ delayed-stream@1.0.0: {}
+
delegates@1.0.0: {}
denque@2.1.0: {}
@@ -8028,11 +8123,19 @@ snapshots:
flatted@3.3.1: {}
+ follow-redirects@1.15.6: {}
+
foreground-child@3.2.1:
dependencies:
cross-spawn: 7.0.3
signal-exit: 4.1.0
+ form-data@4.0.0:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ mime-types: 2.1.35
+
fraction.js@4.3.7: {}
fresh@0.5.2: {}
@@ -8262,7 +8365,7 @@ snapshots:
jiti-v1: jiti@1.21.6
pathe: 1.1.2
pkg-types: 1.1.3
- tsx: 4.16.2
+ tsx: 4.17.0
transitivePeerDependencies:
- supports-color
@@ -8397,6 +8500,14 @@ snapshots:
jiti@2.0.0-beta.2: {}
+ joi@17.13.3:
+ dependencies:
+ '@hapi/hoek': 9.3.0
+ '@hapi/topo': 5.1.0
+ '@sideway/address': 4.1.5
+ '@sideway/formula': 3.0.1
+ '@sideway/pinpoint': 2.0.0
+
js-tokens@4.0.0: {}
js-tokens@9.0.0: {}
@@ -8626,6 +8737,8 @@ snapshots:
dependencies:
brace-expansion: 2.0.1
+ minimist@1.2.8: {}
+
minipass@3.3.6:
dependencies:
yallist: 4.0.0
@@ -9288,6 +9401,8 @@ snapshots:
protocols@2.0.1: {}
+ proxy-from-env@1.1.0: {}
+
punycode@2.3.1: {}
queue-microtask@1.2.3: {}
@@ -9432,6 +9547,10 @@ snapshots:
dependencies:
queue-microtask: 1.2.3
+ rxjs@7.8.1:
+ dependencies:
+ tslib: 2.6.3
+
safe-buffer@5.1.2: {}
safe-buffer@5.2.1: {}
@@ -9754,9 +9873,9 @@ snapshots:
tslib@2.6.3: {}
- tsx@4.16.2:
+ tsx@4.17.0:
dependencies:
- esbuild: 0.21.5
+ esbuild: 0.23.0
get-tsconfig: 4.7.6
optionalDependencies:
fsevents: 2.3.3
@@ -10192,6 +10311,16 @@ snapshots:
optionalDependencies:
typescript: 5.5.4
+ wait-on@7.2.0:
+ dependencies:
+ axios: 1.7.4
+ joi: 17.13.3
+ lodash: 4.17.21
+ minimist: 1.2.8
+ rxjs: 7.8.1
+ transitivePeerDependencies:
+ - debug
+
watchpack@2.4.1:
dependencies:
glob-to-regexp: 0.4.1