From 14811b56790b2a252727d23c2c8d381a5ca6d25e Mon Sep 17 00:00:00 2001 From: vimcaw Date: Sat, 13 Mar 2021 14:29:54 +0800 Subject: [PATCH 1/5] feat: support `ignore` option for glob import --- docs/guide/features.md | 15 ++++ .../glob-import/__tests__/glob-import.spec.ts | 14 ++++ packages/playground/glob-import/index.html | 33 ++++++--- packages/vite/client.d.ts | 6 +- packages/vite/src/node/importGlob.ts | 69 +++++++++++++++++-- 5 files changed, 117 insertions(+), 20 deletions(-) diff --git a/docs/guide/features.md b/docs/guide/features.md index 7f6dfaa2df82b7..97bc0b46dfc700 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -248,6 +248,21 @@ const modules = { } ``` +You can also pass the second argument to ignore some files: + +```js +const modules = import.meta.glob('./dir/*.js', '**/b*.*') +``` + +That will ignore all files whose name starts with `b`, the file `./dir/bar.js` will be ignored, the above will be transformed into the following: + +```js +// code produced by vite +const modules = { + './dir/foo.js': () => import('./dir/foo.js') +} +``` + Note that: - This is a Vite-only feature and is not a web or ES standard. diff --git a/packages/playground/glob-import/__tests__/glob-import.spec.ts b/packages/playground/glob-import/__tests__/glob-import.spec.ts index 15ffb4c7d97ede..d8023d22ab7198 100644 --- a/packages/playground/glob-import/__tests__/glob-import.spec.ts +++ b/packages/playground/glob-import/__tests__/glob-import.spec.ts @@ -44,6 +44,14 @@ const allResult = { msg: 'bar' } } +const resultWithIgnoring = { + '/dir/foo.js': { + msg: 'foo' + }, + '/dir/index.js': { + modules: filteredResult + } +} test('should work', async () => { expect(await page.textContent('.result')).toBe( @@ -51,6 +59,12 @@ test('should work', async () => { ) }) +test('option `ignore` should work', async () => { + expect(await page.textContent('.result-option-ignore')).toBe( + JSON.stringify(resultWithIgnoring, null, 2) + ) +}) + if (!isBuild) { test('hmr for adding/removing files', async () => { addFile('dir/a.js', '') diff --git a/packages/playground/glob-import/index.html b/packages/playground/glob-import/index.html index b38a194e21b4f2..f1a139e46334be 100644 --- a/packages/playground/glob-import/index.html +++ b/packages/playground/glob-import/index.html @@ -1,21 +1,32 @@

+

 
 
 
diff --git a/packages/vite/client.d.ts b/packages/vite/client.d.ts
index ef8e1582e1a92f..28cee931817aa0 100644
--- a/packages/vite/client.d.ts
+++ b/packages/vite/client.d.ts
@@ -26,7 +26,8 @@ interface ImportMeta {
   readonly env: ImportMetaEnv
 
   glob(
-    pattern: string
+    pattern: string,
+    ignore?: string
   ): Record<
     string,
     () => Promise<{
@@ -35,7 +36,8 @@ interface ImportMeta {
   >
 
   globEager(
-    pattern: string
+    pattern: string,
+    ignore?: string
   ): Record<
     string,
     {
diff --git a/packages/vite/src/node/importGlob.ts b/packages/vite/src/node/importGlob.ts
index f14d7bd8bfddda..bbfa06345d482b 100644
--- a/packages/vite/src/node/importGlob.ts
+++ b/packages/vite/src/node/importGlob.ts
@@ -36,7 +36,7 @@ export async function transformImportGlob(
   importer = cleanUrl(importer)
   const importerBasename = path.basename(importer)
 
-  let [pattern, endIndex] = lexGlobPattern(source, pos)
+  let [pattern, ignore, endIndex] = lexGlobPattern(source, pos)
   if (!pattern.startsWith('.') && !pattern.startsWith('/')) {
     throw err(`pattern must start with "." or "/" (relative to project root)`)
   }
@@ -59,7 +59,7 @@ export async function transformImportGlob(
   }
   const files = glob.sync(pattern, {
     cwd: base,
-    ignore: ['**/node_modules/**']
+    ignore: ['**/node_modules/**', ...(ignore ? [ignore] : [])]
   })
   const imports: string[] = []
   let importsString = ``
@@ -110,14 +110,30 @@ const enum LexerState {
   inCall,
   inSingleQuoteString,
   inDoubleQuoteString,
-  inTemplateString
+  inTemplateString,
+  inComma
 }
 
-function lexGlobPattern(code: string, pos: number): [string, number] {
+function lexGlobPattern(
+  code: string,
+  pos: number
+): [string, string | undefined, number] {
+  const startPos = code.indexOf(`(`, pos) + 1
+  const [pattern, endIndexOfPattern] = lexString(code, startPos)
+  const posOfIgnorePattern = findNextArgumentPos(code, endIndexOfPattern)
+  const [ignore, endIndex] =
+    posOfIgnorePattern === -1
+      ? [undefined, endIndexOfPattern]
+      : lexString(code, posOfIgnorePattern)
+
+  return [pattern, ignore, code.indexOf(`)`, endIndex) + 1]
+}
+
+function lexString(code: string, pos: number): [string, number] {
   let state = LexerState.inCall
   let pattern = ''
 
-  let i = code.indexOf(`(`, pos) + 1
+  let i = pos
   outer: for (; i < code.length; i++) {
     const char = code.charAt(i)
     switch (state) {
@@ -159,12 +175,51 @@ function lexGlobPattern(code: string, pos: number): [string, number] {
         throw new Error('unknown import.meta.glob lexer state')
     }
   }
-  return [pattern, code.indexOf(`)`, i) + 1]
+  return [pattern, i + 1]
+}
+
+// it will return -1 if not found next argument
+function findNextArgumentPos(code: string, pos: number): number {
+  let state: LexerState.inComma | undefined = undefined
+  let i = pos
+  outer: for (; i < code.length; i++) {
+    const char = code.charAt(i)
+    switch (state) {
+      case undefined:
+        if (char === ',') {
+          state = LexerState.inComma
+        } else if (char === ')') {
+          i = -1
+          break outer
+        } else if (/\s/.test(char)) {
+          continue
+        } else {
+          error(i)
+        }
+        break
+      case LexerState.inComma:
+        if (char === `'`) {
+          break outer
+        } else if (char === `"`) {
+          break outer
+        } else if (char === '`') {
+          break outer
+        } else if (/\s/.test(char)) {
+          continue
+        } else {
+          error(i)
+        }
+        break
+      default:
+        throw new Error('unknown import.meta.glob lexer state')
+    }
+  }
+  return i
 }
 
 function error(pos: number) {
   const err = new Error(
-    `import.meta.glob() can only accept string literals.`
+    `import.meta.glob() can only accept one or two string literals.`
   ) as RollupError
   err.pos = pos
   throw err

From 742d0cac5dae4e46451b92c72e2bec05f7bef910 Mon Sep 17 00:00:00 2001
From: vimcaw 
Date: Sat, 13 Mar 2021 17:59:04 +0800
Subject: [PATCH 2/5] fix(hmr): add exclusion for `ignore` option of glob
 import

---
 packages/vite/src/node/importGlob.ts             | 2 ++
 packages/vite/src/node/plugins/importAnalysis.ts | 6 ++++--
 packages/vite/src/node/server/hmr.ts             | 4 ++--
 packages/vite/src/node/server/index.ts           | 1 +
 4 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/packages/vite/src/node/importGlob.ts b/packages/vite/src/node/importGlob.ts
index bbfa06345d482b..03a03db652af47 100644
--- a/packages/vite/src/node/importGlob.ts
+++ b/packages/vite/src/node/importGlob.ts
@@ -23,6 +23,7 @@ export async function transformImportGlob(
   endIndex: number
   isEager: boolean
   pattern: string
+  ignore?: string
   base: string
 }> {
   const isEager = source.slice(pos, pos + 21) === 'import.meta.globEager'
@@ -102,6 +103,7 @@ export async function transformImportGlob(
     endIndex,
     isEager,
     pattern,
+    ignore,
     base
   }
 }
diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts
index f12a0d999db270..d97a389b0f38b1 100644
--- a/packages/vite/src/node/plugins/importAnalysis.ts
+++ b/packages/vite/src/node/plugins/importAnalysis.ts
@@ -283,7 +283,8 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
               exp,
               endIndex,
               base,
-              pattern
+              pattern,
+              ignore
             } = await transformImportGlob(
               source,
               start,
@@ -298,7 +299,8 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
             server._globImporters[importerModule.file!] = {
               module: importerModule,
               base,
-              pattern
+              pattern,
+              ignore
             }
           }
           continue
diff --git a/packages/vite/src/node/server/hmr.ts b/packages/vite/src/node/server/hmr.ts
index 93e7e37f71ce75..08b4d6e4e07a98 100644
--- a/packages/vite/src/node/server/hmr.ts
+++ b/packages/vite/src/node/server/hmr.ts
@@ -167,9 +167,9 @@ export async function handleFileAddUnlink(
   } else {
     const modules = []
     for (const i in server._globImporters) {
-      const { module, base, pattern } = server._globImporters[i]
+      const { module, base, pattern, ignore } = server._globImporters[i]
       const relative = path.relative(base, file)
-      if (match(relative, pattern)) {
+      if (match(relative, pattern) && (!ignore || !match(relative, ignore))) {
         modules.push(module)
       }
     }
diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts
index 538994c2903941..7721a35479acf8 100644
--- a/packages/vite/src/node/server/index.ts
+++ b/packages/vite/src/node/server/index.ts
@@ -240,6 +240,7 @@ export interface ViteDevServer {
     {
       base: string
       pattern: string
+      ignore?: string
       module: ModuleNode
     }
   >

From cd0cc998f795f32ece2daf94876b9a0b148c274d Mon Sep 17 00:00:00 2001
From: vimcaw 
Date: Sun, 14 Mar 2021 09:54:45 +0800
Subject: [PATCH 3/5] test: refactor test of glob import to avoid some hmr bug

---
 .../glob-import/__tests__/glob-import.spec.ts | 63 +++++++++++++------
 .../playground/glob-import/dir/_ignored.js    |  1 +
 packages/playground/glob-import/dir/index.js  |  2 +-
 packages/playground/glob-import/index.html    | 33 ++++------
 4 files changed, 57 insertions(+), 42 deletions(-)
 create mode 100644 packages/playground/glob-import/dir/_ignored.js

diff --git a/packages/playground/glob-import/__tests__/glob-import.spec.ts b/packages/playground/glob-import/__tests__/glob-import.spec.ts
index d8023d22ab7198..8f6e744f186298 100644
--- a/packages/playground/glob-import/__tests__/glob-import.spec.ts
+++ b/packages/playground/glob-import/__tests__/glob-import.spec.ts
@@ -29,6 +29,9 @@ const json = isBuild
     }
 
 const allResult = {
+  '/dir/_ignored.js': {
+    msg: 'ignored'
+  },
   // JSON file should be properly transformed
   '/dir/baz.json': json,
   '/dir/foo.js': {
@@ -44,14 +47,6 @@ const allResult = {
     msg: 'bar'
   }
 }
-const resultWithIgnoring = {
-  '/dir/foo.js': {
-    msg: 'foo'
-  },
-  '/dir/index.js': {
-    modules: filteredResult
-  }
-}
 
 test('should work', async () => {
   expect(await page.textContent('.result')).toBe(
@@ -59,20 +54,50 @@ test('should work', async () => {
   )
 })
 
-test('option `ignore` should work', async () => {
-  expect(await page.textContent('.result-option-ignore')).toBe(
-    JSON.stringify(resultWithIgnoring, null, 2)
-  )
-})
-
 if (!isBuild) {
   test('hmr for adding/removing files', async () => {
-    addFile('dir/a.js', '')
+    addFile('dir/+a.js', '')
+    await untilUpdated(
+      () => page.textContent('.result'),
+      JSON.stringify(
+        {
+          '/dir/+a.js': {},
+          ...allResult
+        },
+        null,
+        2
+      )
+    )
+
+    // edit the added file
+    editFile('dir/+a.js', () => 'export const msg ="a"')
+    await untilUpdated(
+      () => page.textContent('.result'),
+      JSON.stringify(
+        {
+          '/dir/+a.js': {
+            msg: 'a'
+          },
+          ...allResult
+        },
+        null,
+        2
+      )
+    )
+
+    removeFile('dir/+a.js')
+    await untilUpdated(
+      () => page.textContent('.result'),
+      JSON.stringify(allResult, null, 2)
+    )
+  })
+  test('hmr for adding/removing files with ignore option', async () => {
+    addFile('dir/_a.js', '')
     await untilUpdated(
       () => page.textContent('.result'),
       JSON.stringify(
         {
-          '/dir/a.js': {},
+          '/dir/_a.js': {},
           ...allResult
         },
         null,
@@ -81,12 +106,12 @@ if (!isBuild) {
     )
 
     // edit the added file
-    editFile('dir/a.js', () => 'export const msg ="a"')
+    editFile('dir/_a.js', () => 'export const msg ="a"')
     await untilUpdated(
       () => page.textContent('.result'),
       JSON.stringify(
         {
-          '/dir/a.js': {
+          '/dir/_a.js': {
             msg: 'a'
           },
           ...allResult
@@ -96,7 +121,7 @@ if (!isBuild) {
       )
     )
 
-    removeFile('dir/a.js')
+    removeFile('dir/_a.js')
     await untilUpdated(
       () => page.textContent('.result'),
       JSON.stringify(allResult, null, 2)
diff --git a/packages/playground/glob-import/dir/_ignored.js b/packages/playground/glob-import/dir/_ignored.js
new file mode 100644
index 00000000000000..26924e0d8414c6
--- /dev/null
+++ b/packages/playground/glob-import/dir/_ignored.js
@@ -0,0 +1 @@
+export const msg = 'ignored'
diff --git a/packages/playground/glob-import/dir/index.js b/packages/playground/glob-import/dir/index.js
index 7adc59a0b14f5e..bba7a299b166fe 100644
--- a/packages/playground/glob-import/dir/index.js
+++ b/packages/playground/glob-import/dir/index.js
@@ -1,3 +1,3 @@
-const modules = import.meta.globEager('./*.js')
+const modules = import.meta.globEager('./*.js', './_*.js')
 
 export { modules }
diff --git a/packages/playground/glob-import/index.html b/packages/playground/glob-import/index.html
index f1a139e46334be..b38a194e21b4f2 100644
--- a/packages/playground/glob-import/index.html
+++ b/packages/playground/glob-import/index.html
@@ -1,32 +1,21 @@
 

-

 
 
 

From 9744ba17f44f5e424dd16e60daaa1b5b024ed8be Mon Sep 17 00:00:00 2001
From: vimcaw 
Date: Thu, 18 Mar 2021 13:02:20 +0800
Subject: [PATCH 4/5] test: normalize the result order of glob importer to
 avoid different test results in different environments

---
 .../glob-import/__tests__/glob-import.spec.ts | 42 ++++++++---------
 packages/playground/glob-import/dir/index.js  |  4 +-
 packages/playground/glob-import/index.html    |  3 +-
 .../playground/glob-import/sortObjectDeep.js  | 15 ++++++
 packages/playground/testUtils.ts              | 46 +++++++++++++++++++
 5 files changed, 84 insertions(+), 26 deletions(-)
 create mode 100644 packages/playground/glob-import/sortObjectDeep.js

diff --git a/packages/playground/glob-import/__tests__/glob-import.spec.ts b/packages/playground/glob-import/__tests__/glob-import.spec.ts
index 8f6e744f186298..49a69efbe5a744 100644
--- a/packages/playground/glob-import/__tests__/glob-import.spec.ts
+++ b/packages/playground/glob-import/__tests__/glob-import.spec.ts
@@ -3,7 +3,8 @@ import {
   editFile,
   isBuild,
   removeFile,
-  untilUpdated
+  untilUpdated,
+  sortObjectDeep
 } from '../../testUtils'
 
 const filteredResult = {
@@ -14,21 +15,14 @@ const filteredResult = {
 
 // json exports key order is altered during build, but it doesn't matter in
 // terms of behavior since module exports are not ordered anyway
-const json = isBuild
-  ? {
-      msg: 'baz',
-      default: {
-        msg: 'baz'
-      }
-    }
-  : {
-      default: {
-        msg: 'baz'
-      },
-      msg: 'baz'
-    }
+const json = {
+  msg: 'baz',
+  default: {
+    msg: 'baz'
+  }
+}
 
-const allResult = {
+const allResult = sortObjectDeep({
   '/dir/_ignored.js': {
     msg: 'ignored'
   },
@@ -46,7 +40,7 @@ const allResult = {
     },
     msg: 'bar'
   }
-}
+})
 
 test('should work', async () => {
   expect(await page.textContent('.result')).toBe(
@@ -60,10 +54,10 @@ if (!isBuild) {
     await untilUpdated(
       () => page.textContent('.result'),
       JSON.stringify(
-        {
+        sortObjectDeep({
           '/dir/+a.js': {},
           ...allResult
-        },
+        }),
         null,
         2
       )
@@ -74,12 +68,12 @@ if (!isBuild) {
     await untilUpdated(
       () => page.textContent('.result'),
       JSON.stringify(
-        {
+        sortObjectDeep({
           '/dir/+a.js': {
             msg: 'a'
           },
           ...allResult
-        },
+        }),
         null,
         2
       )
@@ -96,10 +90,10 @@ if (!isBuild) {
     await untilUpdated(
       () => page.textContent('.result'),
       JSON.stringify(
-        {
+        sortObjectDeep({
           '/dir/_a.js': {},
           ...allResult
-        },
+        }),
         null,
         2
       )
@@ -110,12 +104,12 @@ if (!isBuild) {
     await untilUpdated(
       () => page.textContent('.result'),
       JSON.stringify(
-        {
+        sortObjectDeep({
           '/dir/_a.js': {
             msg: 'a'
           },
           ...allResult
-        },
+        }),
         null,
         2
       )
diff --git a/packages/playground/glob-import/dir/index.js b/packages/playground/glob-import/dir/index.js
index bba7a299b166fe..ab2c9b15418f6c 100644
--- a/packages/playground/glob-import/dir/index.js
+++ b/packages/playground/glob-import/dir/index.js
@@ -1,3 +1,5 @@
-const modules = import.meta.globEager('./*.js', './_*.js')
+import sortObjectDeep from '../sortObjectDeep'
+
+const modules = sortObjectDeep(import.meta.globEager('./*.js', './_*.js'))
 
 export { modules }
diff --git a/packages/playground/glob-import/index.html b/packages/playground/glob-import/index.html
index b38a194e21b4f2..c9eda348d3629e 100644
--- a/packages/playground/glob-import/index.html
+++ b/packages/playground/glob-import/index.html
@@ -2,7 +2,8 @@