Skip to content

Commit

Permalink
feat: make error happy
Browse files Browse the repository at this point in the history
  • Loading branch information
nonzzz committed Jul 21, 2023
1 parent d2356a4 commit ba792c6
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 88 deletions.
6 changes: 5 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
module.exports = {
extends: ['kagura/typescript'],
rules: {
'@typescript-eslint/space-infix-ops': 'error'
'@typescript-eslint/space-infix-ops': 'error',
'@typescript-eslint/type-annotation-spacing': ['error', {
before: false,
after: true
}]
}
}
100 changes: 91 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,99 @@ export default defineConfig({
})
```

### Options
## Options

| params | type | default | description |
| ---------------------- | --------------------------------------------- | ----------------- | -------------------------------------------------------------- |
| `include` | `string \| RegExp \| Array<string \| RegExp>` | `/\.(mjs|js|ts|vue|jsx|tsx)(\?.*|)$/` | Include all assets matching any of these conditions. |
| `exclude` | `string \| RegExp \| Array<string \| RegExp>` | `-` | Exclude all assets matching any of these conditions. |
| `url` | `string` | `jsdelivr` | cdn source url |
| `modules` | `Array<string>\| Array<TrackModule>` | `[]` | modules to be processed |
| `transform` | `()=>InjectVisitor` | `-` | Transform can replace the capture result. and rewrite them. |
| `logLevel` | `'slient'|'warn'` | `warn` | Adjust console output verbosity |
- [`include`](#include)
- [`exclude`](#exclude)
- [`modules`](#modules)
- [`url`](#url)
- [`transform`](#transform)
- [`logLevel`](#logLevel)

### include

Type:

```ts

type FilterPattern = ReadonlyArray<string | RegExp> | string | RegExp | null

```
Default: `/\.(mjs|js|ts|vue|jsx|tsx)(\?.*|)$/`

Include all assets matching any of these conditions.

### exclude

Type:

```ts

type FilterPattern = ReadonlyArray<string | RegExp> | string | RegExp | null

```
Default: `undefined`

Exclude all assets matching any of these conditions.

### modules

Type:

```ts

interface TrackModule {
name: string
global?: string
spare?: Array<string> | string
relativeModule?: string
}

interface IModule extends TrackModule{
[prop: string]: any
}

type Modules = Array<IModule | string>

```
Default: `[]`

Modules to be processed. Details see [Modules](./docs/Modules.md).

### url

Type: string

Default: `https://cdn.jsdelivr.net/npm/`

CDN url. Details see [URL](./docs/URL.md).

### transform

Type:

```ts

interface InjectVisitor {
script?: (node: ScriptNode)=> void
link?: (node: LinkNode)=> void
}

type Trasnform = ()=> InjectVisitor

```

Default: `undefined`

Transform is a overwrite.

### logLevel

Type: `slient` | `warn`

Default: `warn`

Adjust console output verbosity

### Acknowledgements

Expand Down
15 changes: 15 additions & 0 deletions docs/Modules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Modules

Module accept `string[]` or `IModule[]`.

## String

When you pass `string[]` it will be transform as `IModule[]`


## IModule

- name (package entry name)
- global (pacakge global name)
- spare (links that need to be bind to the page)
- relativeModule (If scanner error, try it)
13 changes: 13 additions & 0 deletions docs/URL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# URL

`vite-plugin-cdn2` provide two preset source. `jsdelivr` and `unpkg`. you can using it like this way.

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

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

```

Then all of source will bind with unpkg.
2 changes: 1 addition & 1 deletion example/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default defineConfig(({ command }) => {
Components({ resolvers: [VarletUIResolver()] }),
{
...cdn({
modules: ['vue', 'vue-demi', 'pinia', '@varlet/ui', 'axios']
modules: ['vue', 'vue-demi', 'pinia', '@varlet/ui', 'axios', 'react']
}),
apply: command
},
Expand Down
36 changes: 18 additions & 18 deletions src/code-gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import type { NodePath } from '@babel/core'
import type { ModuleInfo } from './interface'


function isTopLevelCalled(p:NodePath) {
function isTopLevelCalled(p: NodePath) {
return t.isProgram(p.parent) || t.isExportDefaultDeclaration(p.parent) || t.isExportNamedDeclaration(p.parent)
}

export class CodeGen {
private dependencies:Map<string, ModuleInfo>
injectDependencies(dependencies:Map<string, ModuleInfo>) {
private dependencies: Map<string, ModuleInfo>
injectDependencies(dependencies: Map<string, ModuleInfo>) {
this.dependencies = dependencies
}

filter(code:string, id:string) {
filter(code: string, id: string) {
const { output } = esModuleLexer({ input: [{ filename: id, code }] })
if (!len(output)) return false
const { imports } = output[0]
Expand All @@ -27,7 +27,7 @@ export class CodeGen {
return false
}

private scanImportDeclarationAndRecord(path:NodePath<t.ImportDeclaration>, references:Map<string, string>) {
private scanImportDeclarationAndRecord(path: NodePath<t.ImportDeclaration>, references: Map<string, string>) {
const { global: globalName } = this.dependencies.get(path.node.source.value)
for (const specifier of path.node.specifiers) {
switch (specifier.type) {
Expand All @@ -43,14 +43,14 @@ export class CodeGen {
}
}

private overWriteExportNamedDeclaration(path:NodePath<t.ExportNamedDeclaration>, references:Map<string, string>) {
const nodes:Array<t.VariableDeclarator | t.ObjectExpression | t.MemberExpression> = []
const natives:Array<t.ExportSpecifier> = []
private overWriteExportNamedDeclaration(path: NodePath<t.ExportNamedDeclaration>, references: Map<string, string>) {
const nodes: Array<t.VariableDeclarator | t.ObjectExpression | t.MemberExpression> = []
const natives: Array<t.ExportSpecifier> = []
const hasBindings = path.node.source
const globalName = hasBindings ? this.dependencies.get(path.node.source.value).global : ''
const bindings:Set<string> = hasBindings ? this.dependencies.get(path.node.source.value).bindings : new Set()
const bindings: Set<string> = hasBindings ? this.dependencies.get(path.node.source.value).bindings : new Set()

const scanNamedExportsWithSource = (l:t.Identifier, e:t.Identifier, specifier:t.ExportSpecifier) => {
const scanNamedExportsWithSource = (l: t.Identifier, e: t.Identifier, specifier: t.ExportSpecifier) => {
if (!bindings.size) return
if (l.name === 'default' && l.name !== e.name) {
const memberExpression = (p) => t.memberExpression(t.identifier(globalName), t.identifier(p))
Expand Down Expand Up @@ -82,7 +82,7 @@ export class CodeGen {
natives.push(specifier)
}

const scanNamedExportsWithoutSource = (l:t.Identifier, e:t.Identifier, specifier:t.ExportSpecifier) => {
const scanNamedExportsWithoutSource = (l: t.Identifier, e: t.Identifier, specifier: t.ExportSpecifier) => {
if (references.has(l.name)) {
const [o, p] = references.get(l.name).split('.')
if (e.name === 'default') {
Expand Down Expand Up @@ -130,8 +130,8 @@ export class CodeGen {
// export { A , B } from 'module'
// export * as default from 'module'
// export * as xx from 'module'
const variableDeclaratorNodes = nodes.filter((node):node is t.VariableDeclarator => node.type === 'VariableDeclarator')
const objectOrMemberExpression = nodes.filter((node):node is t.ObjectExpression | t.MemberExpression => node.type !== 'VariableDeclarator')
const variableDeclaratorNodes = nodes.filter((node): node is t.VariableDeclarator => node.type === 'VariableDeclarator')
const objectOrMemberExpression = nodes.filter((node): node is t.ObjectExpression | t.MemberExpression => node.type !== 'VariableDeclarator')
if (len(objectOrMemberExpression)) {
const exportDefaultDeclaration = t.exportDefaultDeclaration(objectOrMemberExpression[0])
if (len(variableDeclaratorNodes) || len(natives)) {
Expand All @@ -152,8 +152,8 @@ export class CodeGen {
}
}

private overWriteExportAllDeclaration(path:NodePath<t.ExportAllDeclaration>) {
const nodes:Array<t.ExportSpecifier> = []
private overWriteExportAllDeclaration(path: NodePath<t.ExportAllDeclaration>) {
const nodes: Array<t.ExportSpecifier> = []
const { bindings } = this.dependencies.get(path.node.source.value)
bindings.forEach((binding) => {
const identifier = t.identifier(binding)
Expand Down Expand Up @@ -203,10 +203,10 @@ export class CodeGen {
}
}

async transform(code:string) {
async transform(code: string) {
const ast = await babelParse(code, { babelrc: false, configFile: false })
const references:Map<string, string> = new Map()
const declarations:Map<string, NodePath<t.Declaration | t.Node>> = new Map()
const references: Map<string, string> = new Map()
const declarations: Map<string, NodePath<t.Declaration | t.Node>> = new Map()
traverse(ast, {
ImportDeclaration: {
enter: (path) => {
Expand Down
3 changes: 1 addition & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ function cdn(opts: CDNPluginOptions = {}): Plugin {
if (logLevel === 'warn') {
scanner.failedModules.forEach((errorMessage, name) => config.logger.error(`vite-plugin-cdn2: ${name} ${errorMessage ? errorMessage : 'resolved failed.Please check it.'}`))
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
} catch (error) {
config.logger.error(error)
}
},
Expand Down
39 changes: 26 additions & 13 deletions src/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,32 @@
import { URL } from 'url'
import { Window } from 'happy-dom'
import { uniq } from './shared'
import type { IIFEModuleInfo, CDNPluginOptions, ScriptNode, LinkNode } from './interface'
import type { CDNPluginOptions, ScriptNode, LinkNode, ModuleInfo, URLFunction } from './interface'

function isScript(url: string) {
return url.split('.').pop() === 'js' ? 'script' : 'link'
}

function makeURL(moduleMeta: IIFEModuleInfo, baseURL:string) {
const { version, name: packageName, relativeModule } = moduleMeta
interface Options {
extra: ModuleInfo,
baseURL: string
}

// [baseURL][version][name]
function replaceURL(p: string, url: string | URLFunction, options: Options) {
const template = typeof url === 'function' ? url(p, options.extra) : url
return template.replace(/\[version\]/, options.extra.version).replace(/\[baseURL\]/, options.baseURL).replace(/\[name\]/, options.extra.name)
}

function makeURL(moduleMeta: ModuleInfo, baseURL: string) {
const { version, name: packageName, relativeModule, url: userURL } = moduleMeta
if (!baseURL) return
return new URL(`${packageName}@${version}/${relativeModule}`, baseURL).href
const u = new URL(`${packageName}@${version}/${relativeModule}`, baseURL).href
if (userURL) return replaceURL(u, userURL, { extra: moduleMeta, baseURL })
return u
}

function makeNode(moduleInfo:IIFEModuleInfo):ScriptNode | LinkNode {
function makeNode(moduleInfo: ModuleInfo): ScriptNode | LinkNode {
return {
tag: 'link',
url: new Set(),
Expand All @@ -24,10 +37,9 @@ function makeNode(moduleInfo:IIFEModuleInfo):ScriptNode | LinkNode {
}

class InjectScript {
private modules:Map<string, LinkNode | ScriptNode>

private modules: Map<string, LinkNode | ScriptNode>
private window: Window
constructor(modules: Map<string, IIFEModuleInfo>, url: string) {
constructor(modules: Map<string, ModuleInfo>, url: string) {
this.modules = this.prepareSource(modules, url)
this.window = new Window()
}
Expand Down Expand Up @@ -73,11 +85,11 @@ class InjectScript {
return document.body.innerHTML
}

private prepareSource(modules: Map<string, IIFEModuleInfo>, baseURL: string) {
const container:Map<string, LinkNode | ScriptNode> = new Map()
private prepareSource(modules: Map<string, ModuleInfo>, baseURL: string) {
const container: Map<string, LinkNode | ScriptNode> = new Map()

const traverseModule = (moduleMeta: IIFEModuleInfo, moduleName: string) => {
const { spare } = moduleMeta
const traverseModule = (moduleMeta: ModuleInfo, moduleName: string) => {
const { spare } = moduleMeta
if (!spare) return
if (Array.isArray(spare)) {
for (const s of uniq(spare)) {
Expand All @@ -101,6 +113,7 @@ class InjectScript {
modules.forEach((meta, moduleName) => {
const node = makeNode(meta)
const url = makeURL(meta, baseURL)
if (!url) return
node.url.add(url)
node.tag = isScript(url)
const mark = `__${moduleName}__${node.tag}__`
Expand All @@ -112,7 +125,7 @@ class InjectScript {
}

export function createInjectScript(
dependModules: Map<string, IIFEModuleInfo>,
dependModules: Map<string, ModuleInfo>,
url: string
) {
return new InjectScript(dependModules, url)
Expand Down
Loading

0 comments on commit ba792c6

Please sign in to comment.