Skip to content

Commit

Permalink
Merge pull request #24 from vite-plugin/v0.7.0
Browse files Browse the repository at this point in the history
V0.7.0
  • Loading branch information
caoxiemeihao committed Apr 30, 2023
2 parents 4eef318 + ddf42eb commit 2e1aa35
Show file tree
Hide file tree
Showing 33 changed files with 583 additions and 432 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -109,5 +109,4 @@ package-lock.json
pnpm-lock.yaml
yarn.lock

/index.cjs
/index.js
/types
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
## 0.7.0 (2023-04-30)

- e9eb5f0 refactor(test): integrate vitest 🌱
- 0c892f1 chore: bump deps
- 750fd4b chore: cleanup
- 4691bcc feat: `glob` files log
- 80f46f0 refactor: cleanup
- 7d6a47f chore: backup `v0.5.3`
- 782db06 docs: v0.7.0
- d3207f3 refactor(build): better scripts
- e69a65a refactor: better support `node_modules` #23
- 62a8cd9 chore: cleanup types

## 0.6.2 (2023-03-12)

- enhancement: support node_modules | #19
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,20 @@ export interface Options {
}
```

#### node_modules

```js
commonjs({
filter(id) {
// `node_modules` is exclude by default, so we need to include it explicitly
// https://github.com/vite-plugin/vite-plugin-commonjs/blob/v0.7.0/src/index.ts#L123-L125
if (id.includes('node_modules/xxx')) {
return true
}
}
})
```

## Cases

[vite-plugin-commonjs/test](https://github.com/vite-plugin/vite-plugin-commonjs/tree/main/test)
Expand Down
14 changes: 14 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,20 @@ export interface Options {
}
```

#### node_modules

```js
commonjs({
filter(id) {
// 默认会排除 `node_modules`,所以必须显式的包含它explicitly
// https://github.com/vite-plugin/vite-plugin-commonjs/blob/v0.7.0/src/index.ts#L123-L125
if (id.includes('node_modules/xxx')) {
return true
}
}
})
```

## 案例

[vite-plugin-commonjs/test](https://github.com/vite-plugin/vite-plugin-commonjs/tree/main/test)
Expand Down
39 changes: 21 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
{
"name": "vite-plugin-commonjs",
"version": "0.6.2",
"version": "0.7.0",
"description": "A pure JavaScript implementation of CommonJs",
"type": "module",
"main": "index.js",
"types": "src",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./index.js",
"require": "./index.cjs"
}
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./*": "./*"
},
"repository": {
"type": "git",
Expand All @@ -20,18 +21,22 @@
"scripts": {
"dev": "vite build --watch",
"build": "vite build",
"test": "vite -c test/vite.config.ts",
"prepublishOnly": "npm run build"
"test": "vitest run",
"types": "tsc",
"prepublishOnly": "npm run test && npm run build"
},
"dependencies": {
"fast-glob": "~3.2.11"
"acorn": "^8.8.2",
"fast-glob": "^3.2.12",
"vite-plugin-dynamic-import": "^1.3.2"
},
"devDependencies": {
"@types/node": "^18.7.14",
"typescript": "^4.7.4",
"vite": "^3.2.0-beta.2",
"vite-plugin-dynamic-import": "^1.2.3",
"vite-plugin-utils": "^0.3.3"
"@types/node": "^18.16.2",
"node-fetch": "^3.3.1",
"typescript": "^4.9.4",
"vite": "^4.3.3",
"vite-plugin-utils": "^0.4.3",
"vitest": "^0.30.1"
},
"keywords": [
"vite",
Expand All @@ -40,8 +45,6 @@
"require"
],
"files": [
"src",
"index.cjs",
"index.js"
"dist"
]
}
21 changes: 5 additions & 16 deletions src/analyze.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { walk } from 'vite-plugin-utils/function'

export type AcornNode<T = any> = import('rollup').AcornNode & Record<string, T>

// ①(🎯): Top-level scope statement types, it also means statements that can be converted
// 顶级作用于语句类型,这种可以被无缝换成 import
export enum TopScopeType {
Expand All @@ -12,11 +10,13 @@ export enum TopScopeType {
}

export interface RequireStatement {
/** CallExpression */
node: AcornNode
ancestors: AcornNode[]
/**
* If require statement located top-level scope ant it is convertible, this will have a value(🎯-①)
* 如果 require 在顶级作用于,并且是可转换 import 的,那么 topScopeNode 将会被赋值
* @deprecated 🤔
*/
topScopeNode?: AcornNode & { type: TopScopeType }
dynamic?:
Expand Down Expand Up @@ -69,7 +69,7 @@ export function analyzer(ast: AcornNode, code: string, id: string): Analyzed {
topScopeNode: dynamic === 'dynamic'
? undefined
: findTopLevelScope(ancestors) as RequireStatement['topScopeNode'],
dynamic: checkDynamicId(node),
dynamic,
})
},
AssignmentExpression(node) {
Expand Down Expand Up @@ -114,22 +114,11 @@ function findTopLevelScope(ancestors: AcornNode[]): AcornNode | undefined {
const ances = ancestors.map(an => an.type).join()
const arr = [...ancestors].reverse()

// TODO: better top-scope detect

if (/Program,ExpressionStatement,(MemberExpression,)?CallExpression$/.test(ances)) {
// Program,ExpressionStatement,CallExpression | require('foo')
// Program,ExpressionStatement,MemberExpression,CallExpression | require('foo').bar
return arr.find(e => e.type === TopScopeType.ExpressionStatement)
}

// TODO(#15): Loose conversion of `exports` is required to get elegant import statements, vice versa.
// 需要松散的 exports 转换,才能得到优雅的 import 语句,反之亦然。
// 🚨-①: Vite also does the same. All statements are imported as `*`, which is simple and easy to implement. :)
// Vite 也是这么做的,所有语句都以 * 导入,即简单又好实现。
return

// At present, "ancestors" contains only one depth of "MemberExpression"
if (/Program,VariableDeclaration,VariableDeclarator,(MemberExpression,)?CallExpression$/.test(ances)) {
// const bar = require('foo').bar
// const { foo, bar: baz } = require('foo')
return arr.find(e => e.type === TopScopeType.VariableDeclaration)
}
}
127 changes: 30 additions & 97 deletions src/dynamic-require.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import path from 'node:path'
import type { ResolvedConfig } from 'vite'
import fastGlob from 'fast-glob'
import {
type Resolved,
Resolve,
dynamicImportToGlob,
mappingPath,
toLooseGlob,
globFiles,
} from 'vite-plugin-dynamic-import'
import { normalizePath, relativeify } from 'vite-plugin-utils/function'
import type { Options } from '.'
import type { AcornNode, Analyzed } from './analyze'
import { normallyImporteeRE } from './utils'
import { normalizePath, COLOURS } from 'vite-plugin-utils/function'
import {
type Options,
TAG,
} from '.'
import type { Analyzed } from './analyze'

export interface DynamicRequireRecord {
node: AcornNode
Expand Down Expand Up @@ -39,33 +38,41 @@ export class DynaimcRequire {
const importCache = new Map<string, string>(/* import-id, import-name */)
const records: DynamicRequireRecord[] = []

for (const req of analyzed.require) {
const { node, dynamic } = req
for (const { dynamic, node } of analyzed.require) {
if (dynamic !== 'dynamic') continue

const globResult = await globFiles(
node,
analyzed.code,
analyzed.id,
this.resolve,
this.options.extensions,
options.dynamic?.loose !== false,
)
const importExpression = analyzed.code.slice(node.start, node.end)
const globResult = await globFiles({
importeeNode: node.arguments[0],
importExpression,
importer: analyzed.id,
resolve: this.resolve,
extensions: this.options.extensions,
loose: options.dynamic?.loose !== false,
})
if (!globResult) continue
const record: DynamicRequireRecord = { node }

let { files, resolved, normally } = globResult
// skip itself
files = files!.filter(f => normalizePath(path.join(path.dirname(id), f)) !== id)
// execute the dynamic.onFiles
options.dynamic?.onFiles && (files = options.dynamic?.onFiles(files, id) || files)

if (normally) {
record.normally = normally
continue
}

if (!files?.length) continue
if (!files?.length) {
console.log(
TAG,
COLOURS.yellow(`no files matched: ${importExpression}\n`),
` file: ${analyzed.id}`,
)
continue
}

// skip itself
files = files.filter(f => normalizePath(path.join(path.dirname(id), f)) !== id)
// execute the dynamic.onFiles
options.dynamic?.onFiles && (files = options.dynamic?.onFiles(files, id) || files)

const maps = mappingPath(
files,
Expand Down Expand Up @@ -108,77 +115,3 @@ ${cases.join('\n')}
return records.length ? records : null
}
}

async function globFiles(
/** ImportExpression */
node: AcornNode,
code: string,
importer: string,
resolve: Resolve,
extensions: string[],
loose = true,
): Promise<{
files?: string[]
resolved?: Resolved
/** After `expressiontoglob()` processing, it may become a normally path */
normally?: string
} | undefined> {
let files: string[]
let resolved: Resolved | undefined
let normally: string

const PAHT_FILL = '####/'
const EXT_FILL = '.extension'
let glob: string | null
let globRaw!: string

glob = await dynamicImportToGlob(
node.arguments[0],
code.slice(node.start, node.end),
async (raw) => {
globRaw = raw
resolved = await resolve.tryResolve(raw, importer)
if (resolved) {
raw = resolved.import.resolved
}
if (!path.extname(raw)) {
// Bypass extension restrict
raw = raw + EXT_FILL
}
if (/^\.\/\*\.\w+$/.test(raw)) {
// Bypass ownDirectoryStarExtension (./*.ext)
raw = raw.replace('./*', `./${PAHT_FILL}*`)
}
return raw
},
)
if (!glob) {
if (normallyImporteeRE.test(globRaw)) {
normally = globRaw
return { normally }
}
return
}

// @ts-ignore
const globs = [].concat(loose ? toLooseGlob(glob) : glob)
.map((g: any) => {
g.includes(PAHT_FILL) && (g = g.replace(PAHT_FILL, ''))
g.endsWith(EXT_FILL) && (g = g.replace(EXT_FILL, ''))
return g
})
const fileGlobs = globs
.map(g => path.extname(g)
? g
// If not ext is not specified, fill necessary extensions
// e.g.
// `./foo/*` -> `./foo/*.{js,ts,vue,...}`
: g + `.{${extensions.map(e => e.replace(/^\./, '')).join(',')}}`
)

files = fastGlob
.sync(fileGlobs, { cwd: /* 🚧-① */path.dirname(importer) })
.map(file => relativeify(file))

return { files, resolved }
}
Loading

0 comments on commit 2e1aa35

Please sign in to comment.