Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CDN import module #54

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 75 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Installation
npm install -D rollup-plugin-external-globals
```

Usage
Usage 1
-----

```js
Expand Down Expand Up @@ -69,6 +69,65 @@ Promise.resolve($)

> **Note:** this plugin only works with import/export syntax. If you are using a module loader transformer e.g. `rollup-plugin-commonjs`, you have to put this plugin *after* the transformer plugin.

Usage 2
-----
```js
import externalGlobals from "rollup-plugin-external-globals";

export default {
input: ["entry.js"],
output: {
dir: "dist",
format: "es"
},
plugins: [
externalGlobals({
jquery: {
name: "$",
esmUrl: "https://cdn.jsdelivr.net/npm/[email protected]/+esm"
}
})
]
};
```

The above config transforms

```js
import jq from "jquery";

console.log(jq(".test"));
```

into

```js
import $ from "https://cdn.jsdelivr.net/npm/[email protected]/+esm";

console.log($(".test"));
```

It also transforms dynamic import:

```js
import("jquery")
.then($ => {
$ = $.default || $;
console.log($(".test"));
});

// transformed
import("https://cdn.jsdelivr.net/npm/[email protected]/+esm")
.then($ => {
$ = $.default || $;
console.log($(".test"));
});
```

> **Note:** when using dynamic import, you should notice that in ES module, the resolved object is aways a module namespace, but the global variable might be not.

> **Note:** this plugin only works with import/export syntax. If you are using a module loader transformer e.g. `rollup-plugin-commonjs`, you have to put this plugin *after* the transformer plugin.

API
----

Expand All @@ -78,7 +137,7 @@ This module exports a single function.

```js
const plugin = createPlugin(
globals: Object | Function,
globals: Object | Function | {name: String, esmUrl?: String},
{
include?: Array,
exclude?: Array,
Expand All @@ -96,6 +155,17 @@ const globals = {
}
```

or `globals` is a `moduleId`/`variableName and (ESM)CDN URL` map,`(ESM)CDN URL` is optional. For example, to map `jquery` module to `{name: "$", esmUrl: "https://cdn.jsdelivr.net/npm/[email protected]/+esm"}`:

```js
const globals = {
jquery: {
name: "$",
esmUrl: "https://cdn.jsdelivr.net/npm/[email protected]/+esm"
}
}
```

or provide a function that takes the `moduleId` and returns the `variableName`.

```js
Expand Down Expand Up @@ -124,6 +194,9 @@ Virtual modules are always transformed.

Changelog
---------
* 0.12.2 (Nov 17, 2024)

- Add: CDN import module function (only supports ESM modules).

* 0.12.1 (Nov 15, 2024)

Expand Down
15 changes: 13 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import type { Plugin } from "rollup";

export type VariableName = string;

export type ModuleConfig = {
/**
* [name] is the global name of the module
*/
name:string;
/**
* [esmUrl] is the CDN URL for the ESM version of the module
*/
esmUrl?:string;
};
/**
* globals is a moduleId/variableName map
* globals is a moduleId/variableName or moduleId/variableName and (ESM)CDN URL map
* or provide a function that takes the moduleId and returns the variableName
*/
export type ModuleNameMap =
| Record<string, string>
| Record<string, string|ModuleConfig>
| ((id: string) => VariableName);

export type ExternalGlobalsOptions = {
Expand Down
10 changes: 10 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,18 @@ function createPlugin(globals, {include, exclude, dynamicWrapper = defaultDynami
if (isGlobalsObj) {
getName = function (name) {
if (Object.prototype.hasOwnProperty.call(globals, name)) {
if(!globals[name]){
throw new TypeError("Missing mandatory option 'globals'");
}
else if(globals[name] instanceof Object&&Object.keys(globals[name]).length === 0){
throw new TypeError("Missing mandatory option 'globals'");
}
return globals[name];
}
else if(Object.keys(globals).length === 0){
throw new TypeError("Missing mandatory option 'globals'");
}

};
} else if (globalsType !== "function") {
throw new TypeError(`Unexpected type of 'globals', got '${globalsType}'`);
Expand Down
53 changes: 43 additions & 10 deletions lib/import-to-globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,26 @@ function analyzeImport(node, importBindings, code, getName, globals) {
if (!name) {
return false;
}
globals.add(name);
let globalName;
if(typeof name === "string"){
globalName=name;
}
else if((name instanceof Object)&&Object.prototype.hasOwnProperty.call(name, "name")){
globalName=name.name;
}
globals.add(globalName);
for (const spec of node.specifiers) {
importBindings.set(spec.local.name, makeGlobalName(
spec.imported ? spec.imported.name : "default",
name
globalName
));
}
code.remove(node.start, node.end);
if(typeof name === "string"||!Object.prototype.hasOwnProperty.call(name, 'esmUrl')){
code.remove(node.start, node.end);
}
else if(Object.prototype.hasOwnProperty.call(name, 'esmUrl')){
code.overwrite(node.start,node.end, `import ${globalName} from "${name.esmUrl}"`, { contentOnly: true });
}
return true;
}

Expand All @@ -32,13 +44,16 @@ function makeGlobalName(prop, name) {
return `${name}.${prop}`;
}

function writeSpecLocal(code, root, spec, name, tempNames, constBindings) {
function writeSpecLocal(code, root, spec, name, tempNames, constBindings,globalValue) {
if (spec.isOverwritten) return;
// we always need an extra assignment for named export statement
// https://github.com/eight04/rollup-plugin-external-globals/issues/19
const localName = `_global_${makeLegalIdentifier(name)}`;
if (!tempNames.has(localName)) {
code.appendRight(root.start, `${constBindings ? "const" : "var"} ${localName} = ${name};\n`);
if(globalValue instanceof Object&&Object.prototype.hasOwnProperty.call(globalValue, "esmUrl")){
code.appendRight(0, `import ${globalValue.name} from "${globalValue.esmUrl}";`)
}
tempNames.add(localName);
}
if (spec.local.name === localName) {
Expand Down Expand Up @@ -81,8 +96,14 @@ function analyzeExportNamed(node, code, getName, tempNames, constBindings) {
return false;
}
for (const spec of node.specifiers) {
const globalName = makeGlobalName(spec.local.name, name);
writeSpecLocal(code, node, spec, globalName, tempNames, constBindings);
let globalName;
if(typeof name === "string"){
globalName=makeGlobalName(spec.local.name, name);
}
else if((name instanceof Object)&&Object.prototype.hasOwnProperty.call(name, "name")){
globalName=makeGlobalName(spec.local.name, name.name);
}
writeSpecLocal(code, node, spec, globalName, tempNames, constBindings,name);
}
if (node.specifiers.length) {
code.overwrite(node.specifiers[node.specifiers.length - 1].end, node.source.end, "}");
Expand Down Expand Up @@ -167,10 +188,12 @@ async function importToGlobals({ast, code, getName, getDynamicWrapper, constBind
if (node.scope) {
scope = node.scope;
}
const source = getDynamicImportSource(node);
let name= source && getName(source);
if (isReference(node, parent)) {
if (bindings.has(node.name) && !scope.contains(node.name)) {
if (parent.type === "ExportSpecifier") {
writeSpecLocal(code, topStatement, parent, bindings.get(node.name), tempNames, constBindings);
writeSpecLocal(code, topStatement, parent, bindings.get(node.name), tempNames, constBindings,name);
} else {
writeIdentifier(code, node, parent, bindings.get(node.name));
}
Expand All @@ -183,9 +206,19 @@ async function importToGlobals({ast, code, getName, getDynamicWrapper, constBind
}
}
}
const source = getDynamicImportSource(node);
const name = source && getName(source);
const dynamicName = name && getDynamicWrapper(name);
let dynamicName;
if(source &&(getName(source) instanceof Object)&&Object.prototype.hasOwnProperty.call(getName(source), "name")){
name = source && getName(source).name;
}
if(source &&(getName(source) instanceof Object)&&Object.prototype.hasOwnProperty.call(getName(source), "esmUrl")){
dynamicName = name && getDynamicWrapper(`"${getName(source).esmUrl}"`);
if(typeof dynamicName==='string'&&/^Promise.resolve/i.test(dynamicName.replace(/\s+/g,''))){
dynamicName =`import("${getName(source).esmUrl}")`;
}
}
else{
dynamicName = name && getDynamicWrapper(name);
}
if (dynamicName) {
writeDynamicImport(code, node, dynamicName);
isTouched = true;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rollup-plugin-external-globals",
"version": "0.12.1",
"version": "0.12.2",
"description": "Transform external imports into global variables like output.globals.",
"keywords": [
"rollup-plugin",
Expand Down