Skip to content

Commit

Permalink
fix: package should work normal (#14)
Browse files Browse the repository at this point in the history
* fix: dynamic import should work

* test: fallback vm test

* feat: vm capture should skip underscore

* fix: typesVersion only exports url

* feat: add debug

* docs: update document

* chore: exmaple add debug mode
  • Loading branch information
nonzzz committed Jul 25, 2023
1 parent bafe9b0 commit 49b7d27
Show file tree
Hide file tree
Showing 15 changed files with 93 additions and 33 deletions.
2 changes: 1 addition & 1 deletion __tests__/code-gen.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import test from 'ava'
import { parse, traverse } from '@babel/core'
import { createCodeGenerator } from '../dist/code-gen'
import { createCodeGenerator } from '../src/code-gen'
import { createScanner } from '../dist/scanner'
import type { ModuleInfo } from '../src/interface'

Expand Down
8 changes: 4 additions & 4 deletions __tests__/inject.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import test from 'ava'
import { len } from '../dist/shared'
import { createInjectScript } from '../dist/inject'
import { jsdelivr } from '../dist/url'
import type { TrackModule } from '../dist'
import { len } from '../src/shared'
import { createInjectScript } from '../src/inject'
import { jsdelivr } from '../src/url'
import type { TrackModule } from '../src'

interface MockIIFEMdoule extends TrackModule{
relativeModule: string
Expand Down
2 changes: 1 addition & 1 deletion __tests__/shared.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fsp from 'fs/promises'
import test from 'ava'
import { len, lookup } from '../dist/shared'
import { len, lookup } from '../src/shared'


test('len', (t) => {
Expand Down
32 changes: 32 additions & 0 deletions __tests__/vm.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import test from 'ava'
import { MAX_CONCURRENT, createConcurrentQueue, createVM } from '../src/vm'

test('native vm', async (t) => {
const vm = createVM()
await vm.run('var nonzzz = \'test plugin\'', { name: 'nonzzz' } as any, (err) => err)
t.is(vm.bindings.has('nonzzz'), true)
})

test('shadow vm', async (t) => {
const vm = createVM()
await vm.run('window.nonzzz = 123', { name: 'nonzzz' } as any, (err) => err)
t.is(vm.bindings.has('nonzzz'), true)
})

test('throw error in vm', async (t) => {
const vm = createVM()
await vm.run('throw new Error(\'error\')', { name: 'nonzzz' } as any, (err) => {
t.is(err?.message, 'nonzzz')
})
})

test('task queue', async (t) => {
const tasks = [() => Promise.resolve(), () => Promise.resolve(), () => Promise.reject(new Error('3'))]

const queue = createConcurrentQueue(MAX_CONCURRENT)
for (const task of tasks) {
queue.enqueue(task)
}
const err = await t.throwsAsync(queue.wait())
t.is(err?.message, '3')
})
2 changes: 1 addition & 1 deletion docs/URL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

```js
import { cdn } from 'vite-plugin-cdn2'
import { unpkg } from 'vite-plugin-cdn2/url'
import { unpkg } from 'vite-plugin-cdn2/url.js'

cdn({url:unpkg,modules:['vue']})

Expand Down
2 changes: 1 addition & 1 deletion example/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "vite-plugin-cdn2-example",
"scripts": {
"dev": "vite",
"dev": "set DEBUG=vite-plugin-cdn2 & vite",
"build": "vite build",
"preview": "vite preview"
},
Expand Down
2 changes: 1 addition & 1 deletion example/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import vue from '@vitejs/plugin-vue'
import { VarletUIResolver } from 'unplugin-vue-components/resolvers'
import Components from 'unplugin-vue-components/vite'
import { cdn } from 'vite-plugin-cdn2'
import { unpkg } from 'vite-plugin-cdn2/url'
import { unpkg } from 'vite-plugin-cdn2/url.js'
import { compression } from 'vite-plugin-compression2'
import Inspect from 'vite-plugin-inspect'

Expand Down
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./url": {
"types":"./dist/url.d.ts",
"./url.js": {
"types": "./dist/url.d.ts",
"import": "./dist/url.mjs",
"require": "./dist/url.js"
},
"./*": "./*"
},
"typesVersions": {
"*": {
"*": [
"./dist/*"
"url.js": [
"./dist/url.d.ts"
]
}
},
Expand All @@ -57,6 +57,7 @@
"devDependencies": {
"@rollup/plugin-json": "^6.0.0",
"@types/babel__core": "^7.20.1",
"@types/debug": "^4.1.8",
"ava": "^5.2.0",
"c8": "^7.12.0",
"eslint": "^8.23.1",
Expand All @@ -72,6 +73,7 @@
"@babel/core": "^7.22.5",
"@rollup/pluginutils": "^5.0.2",
"@types/estree": "^1.0.1",
"debug": "^4.3.4",
"happy-dom": "^6.0.4",
"rs-module-lexer": "^1.0.0"
},
Expand Down
2 changes: 1 addition & 1 deletion src/code-gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class CodeGen {
const modules = Array.from(new Set([...imports.map(i => i.n)]))
for (const m of modules) {
if (this.dependencies.has(m)) return true
return false
continue
}
return false
}
Expand Down
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { createFilter } from '@rollup/pluginutils'
import type { Plugin } from 'vite'
import _debug from 'debug'
import { createScanner } from './scanner'
import { createInjectScript } from './inject'
import { createCodeGenerator } from './code-gen'
import { isSupportThreads } from './shared'
import { jsdelivr } from './url'
import type { CDNPluginOptions } from './interface'

const debug = _debug('vite-plugin-cdn2')

function cdn(opts: CDNPluginOptions = {}): Plugin {
const { modules = [], url = jsdelivr, include = /\.(mjs|js|ts|vue|jsx|tsx)(\?.*|)$/, exclude, logLevel = 'warn', resolve: resolver } = opts
const filter = createFilter(include, exclude)
Expand All @@ -20,7 +23,9 @@ function cdn(opts: CDNPluginOptions = {}): Plugin {
const [isSupport, version] = isSupportThreads()
try {
if (!isSupport) throw new Error(`vite-plugin-cdn2 can't work with nodejs ${version}.`)
debug('start scanning')
await scanner.scanAllDependencies()
debug('scanning done', scanner.dependencies)
generator.injectDependencies(scanner.dependencies)
if (logLevel === 'warn') {
scanner.failedModules.forEach((errorMessage, name) => config.logger.error(`vite-plugin-cdn2: ${name} ${errorMessage ? errorMessage : 'resolved failed.Please check it.'}`))
Expand Down
8 changes: 6 additions & 2 deletions src/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ import fsp from 'fs/promises'
import worker_threads from 'worker_threads'
import type { MessagePort } from 'worker_threads'
import { MAX_CONCURRENT, createConcurrentQueue, createVM } from './vm'
import { is, len, lookup } from './shared'
import { _import, is, len, lookup } from './shared'
import type { IIFEModuleInfo, IModule, ModuleInfo, ResolverFunction, TrackModule } from './interface'

// This file is a simply dependencies scanner.
// We won't throw any error unless it's an internal thread error(such as pid not equal)
// we consume all expection modules in the plugin itself.
// Notice. This file don't handle any logic with script inject.

// TODO
// We pack this file just to make the test pass. If we migrate to other test framework
// Don't forget remove it.

interface WorkerData {
scannerModule: IModule[]
workerPort: MessagePort
Expand Down Expand Up @@ -83,7 +87,7 @@ async function tryResolveModule(
const code = await fsp.readFile(iifeFilePath, 'utf8')
Object.assign(meta, { name, version, code, relativeModule: iifeRelativePath, ...rest })
}
const pkg = await import(moduleName)
const pkg = await _import(moduleName)
const keys = Object.keys(pkg)
// If it's only exports by default
if (keys.includes('default') && len(keys) !== 1) {
Expand Down
4 changes: 4 additions & 0 deletions src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ export function is(condit: boolean, message: string) {
throw new Error(message)
}
}

// TODO
// If we find the correct dynamic import handing it should be removed.
export const _import = new Function('specifier', 'return import(specifier)')
33 changes: 17 additions & 16 deletions src/vm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,50 +4,51 @@ import { Window } from 'happy-dom'
import { len } from './shared'
import type { ModuleInfo } from './interface'

// Normal. Each umd of iife library only export one module. But some libraries don't
// follow this principle. They export some variable starting with __ like `Vue`,`Element-Plus`
// Variables staring with an underscore are private variables by convention and we shouldn't
// parse them. If they are mulitple variables then we only take the last.

export function createVM() {
const bindings: Map<string, ModuleInfo> = new Map()
const window = new Window()
const context = vm.createContext({})
let _meta: ModuleInfo = null
let id = 0
let callerId = 0

const updateBindings = (name: string, meta: ModuleInfo) => {
bindings.set(meta.name, { ...meta, global: name })
}
const shadow = new Proxy(window, {
set(target, key: string, value, receiver) {
callerId++
if (id === callerId) updateBindings(key, _meta)
Reflect.set(target, key, value, receiver)
return true
}
})

const run = (code: string, meta: ModuleInfo, handler: (err: Error)=> void) => {
_meta = meta
try {
vm.runInContext(code, context)
// TODO
// This is a temporary solution.
// when vm run script it can't run others logic in threads it will directly
// end the function.
// So we need to get the last one using the tag.
// https://github.com/nodejs/help/issues/1378
id = len(Object.keys(context))
const c = Object.keys(context)
Object.assign(shadow, context)
let last = c.pop()
while (last.startsWith('__')) {
last = c.pop()
}
updateBindings(last, meta)
// context free
for (const key in context) {
Reflect.deleteProperty(context, key)
}
_meta = null
callerId = 0
id = 0
} catch (error) {
try {
// In most cases there will only be one variable
window.eval(code)
updateBindings(Object.keys(shadow).pop(), meta)
const c = Object.keys(shadow)
let last = c.pop()
while (last.startsWith('__')) {
last = c.pop()
}
updateBindings(last, meta)
} catch (_) {
handler(new Error(meta.name))
}
Expand Down
2 changes: 1 addition & 1 deletion tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Options } from 'tsup'

export const tsup: Options = {
entry: ['src/*.ts'],
entry: ['src/index.ts', 'src/url.ts', 'src/scanner.ts'],
format: ['cjs', 'esm'],
dts: true,
splitting: true,
Expand Down
12 changes: 12 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,13 @@
dependencies:
"@types/node" "*"

"@types/debug@^4.1.8":
version "4.1.8"
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.8.tgz#cef723a5d0a90990313faec2d1e22aee5eecb317"
integrity sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==
dependencies:
"@types/ms" "*"

"@types/estree@^1.0.0", "@types/estree@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194"
Expand All @@ -553,6 +560,11 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==

"@types/ms@*":
version "0.7.31"
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==

"@types/node@*":
version "18.15.11"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f"
Expand Down

0 comments on commit 49b7d27

Please sign in to comment.