Skip to content

Commit

Permalink
(feat): Add build command by wrapping rollup (#2544)
Browse files Browse the repository at this point in the history
* (feat): Add build command by wrapping rollup

* Modify the plugin and it can download the files too now from cdn

* simplify the build scenario

* use fetch from @jspm/generator

* Update tests for ownname scenario

* fix tests

* Resolve all changes from the PR comments

* use directories as entry and output for result

* updatre documentation for the new commands

* Display warning message if the dependency is failed to resolve
  • Loading branch information
JayaKrishnaNamburu authored Oct 24, 2023
1 parent dcefe25 commit d77f468
Show file tree
Hide file tree
Showing 40 changed files with 3,014 additions and 53 deletions.
2 changes: 1 addition & 1 deletion chompfile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ deps = ['dist/cli.js', 'docs.md']
[[task]]
target = 'dist/cli.js'
deps = ['src/**/*.ts', 'npm:install']
run = 'esbuild src/cli.ts --bundle --platform=node --external:@jspm/generator --external:ora --external:picocolors --external:@babel/core --format=esm --outfile=$TARGET'
run = 'esbuild src/cli.ts --bundle --platform=node --external:fsevents --external:@jspm/generator --external:ora --external:picocolors --external:@babel/core --format=esm --outfile=$TARGET'

[[task]]
name = 'docs'
Expand Down
42 changes: 25 additions & 17 deletions docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,12 +236,12 @@ Providers are used to resolve package _canonical names_ (such as `npm:react@18.2

The following providers are supported:

* `jspm.io`
* `jspm.io#system`
* `nodemodules`
* `esm.sh`
* `unpkg`
* `jsdelivr`
- `jspm.io`
- `jspm.io#system`
- `nodemodules`
- `esm.sh`
- `unpkg`
- `jsdelivr`

Most of these providers will resolve against their corresponding CDNs. For instance, `esm.sh` uses the [esm.sh](https://esm.sh) CDN, `unpkg` uses the [UNPKG](https://unpkg.com) CDN, and so on.

Expand All @@ -259,11 +259,7 @@ Installs `lit` into the import map using the `nodemodules` provider, which maps

```json
{
"env": [
"browser",
"development",
"module"
],
"env": ["browser", "development", "module"],
"imports": {
"lit": "./node_modules/lit/index.js"
},
Expand All @@ -278,7 +274,6 @@ Installs `lit` into the import map using the `nodemodules` provider, which maps
}
```


## Resolutions

Resolutions are used to remap package _names_ to particular package _targets_. For instance, the latest version of one of your secondary dependencies may be broken, and you want to pin it to an older version, or even to a different package altogether. To do this, you can provide one or more `-r` or `--resolution` flags, with arguments `[package_name]=[target_version]` or `[package_name]=[registry]:[name]@[target-range]`. Package specifiers can take the full syntax described under [`jspm install`](#jspm-install).
Expand All @@ -295,17 +290,30 @@ Installs `npm:preact@10.13.2` into the import map under the name `react`. Note t

```json
{
"env": [
"browser",
"development",
"module"
],
"env": ["browser", "development", "module"],
"imports": {
"react": "https://ga.jspm.io/npm:preact@10.13.2/dist/preact.module.js"
}
}
```

### Build

The build command can be used to build a project from the import map, which will include all dependencies by resolving them from CDN against the import map.

The command operates in two modes,

```sh
jspm build ./app.js --output dir
```

Uses default rollup configuration and builds the project with the importmap.

If you would like to use a custom rollup configuration, you can use the `--build-config` flag.

```sh
jspm build --config rollup.config.mjs
```

## Preload Tags and Integrity Attributes

Expand Down
42 changes: 25 additions & 17 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ Providers are used to resolve package _canonical names_ (such as `npm:react@18.2

The following providers are supported:

* `jspm.io`
* `jspm.io#system`
* `nodemodules`
* `esm.sh`
* `unpkg`
* `jsdelivr`
- `jspm.io`
- `jspm.io#system`
- `nodemodules`
- `esm.sh`
- `unpkg`
- `jsdelivr`

Most of these providers will resolve against their corresponding CDNs. For instance, `esm.sh` uses the [esm.sh](https://esm.sh) CDN, `unpkg` uses the [UNPKG](https://unpkg.com) CDN, and so on.

Expand All @@ -57,11 +57,7 @@ Installs `lit` into the import map using the `nodemodules` provider, which maps

```json
{
"env": [
"browser",
"development",
"module"
],
"env": ["browser", "development", "module"],
"imports": {
"lit": "./node_modules/lit/index.js"
},
Expand All @@ -76,7 +72,6 @@ Installs `lit` into the import map using the `nodemodules` provider, which maps
}
```


## Resolutions

Resolutions are used to remap package _names_ to particular package _targets_. For instance, the latest version of one of your secondary dependencies may be broken, and you want to pin it to an older version, or even to a different package altogether. To do this, you can provide one or more `-r` or `--resolution` flags, with arguments `[package_name]=[target_version]` or `[package_name]=[registry]:[name]@[target-range]`. Package specifiers can take the full syntax described under [`jspm install`](#jspm-install).
Expand All @@ -93,17 +88,30 @@ Installs `npm:preact@10.13.2` into the import map under the name `react`. Note t

```json
{
"env": [
"browser",
"development",
"module"
],
"env": ["browser", "development", "module"],
"imports": {
"react": "https://ga.jspm.io/npm:preact@10.13.2/dist/preact.module.js"
}
}
```

### Build

The build command can be used to build a project from the import map, which will include all dependencies by resolving them from CDN against the import map.

The command operates in two modes,

```sh
jspm build ./app.js --output dir
```

Uses default rollup configuration and builds the project with the importmap.

If you would like to use a custom rollup configuration, you can use the `--build-config` flag.

```sh
jspm build --config rollup.config.mjs
```

## Preload Tags and Integrity Attributes

Expand Down
19 changes: 17 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"@jspm/generator": "^1.1.10",
"cac": "^6.7.14",
"ora": "^6.3.0",
"picocolors": "^1.0.0"
"picocolors": "^1.0.0",
"rollup": "^3.29.2"
},
"devDependencies": {
"@antfu/eslint-config": "^0.34.2",
Expand Down
64 changes: 64 additions & 0 deletions src/build/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import path from "node:path";
import process from "node:process";
import { type RollupOptions, rollup } from "rollup";

import { JspmError, exists } from "../utils";
import type { Flags } from "../types";
import { RollupImportmapPlugin } from "./rollup-importmap-plugin";

export default async function build(entry: string, options: Flags) {
if (!entry && !options.config) {
throw new JspmError(`Please provide entry for the build`);
}

let buildConfig: RollupOptions;
let outputOptions: RollupOptions["output"];

if (entry) {
if (!options.output) {
throw new JspmError(`Build output is required when entry is provided`);
}

const entryPath = path.join(process.cwd(), entry);
if ((await exists(entryPath)) === false) {
throw new JspmError(`Entry file does not exist: ${entryPath}`);
}
buildConfig = {
input: entryPath,
plugins: [RollupImportmapPlugin(options)],
};

outputOptions = {
dir: path.join(process.cwd(), options.output),
};
}

if (options.config) {
const buildConfigPath = path.join(process.cwd(), options.config);
if ((await exists(buildConfigPath)) === false) {
throw new JspmError(
`Build config file does not exist: ${buildConfigPath}`
);
}
const rollupConfig = await import(buildConfigPath)
.then((mod) => mod.default)
.catch((err) => {
throw new JspmError(`Failed to load build config: ${err}`);
});

if ("output" in rollupConfig) {
outputOptions = rollupConfig.output;
}

buildConfig = {
...rollupConfig,
plugins: [
...(rollupConfig?.plugins || []),
RollupImportmapPlugin(options),
],
};
}

const builder = await rollup(buildConfig);
await builder.write({ format: "esm", ...outputOptions });
}
68 changes: 68 additions & 0 deletions src/build/rollup-importmap-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Plugin } from "rollup";
import fs from "node:fs/promises";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { fetch } from "@jspm/generator";
import { Flags } from "../types";
import { getGenerator, JspmError } from "../utils";

const isValidUrl = (url: string) => {
try {
return Boolean(new URL(url));
} catch (e) {
return false;
}
};

export const RollupImportmapPlugin = async (flags: Flags): Promise<Plugin> => {
/*
Install without a freeze might bump the versions.
We would like to maintian 1:1 on what users defined in importmap.
*/
const generator = await getGenerator({ ...flags, freeze: true });
await generator.install();

return {
name: "rollup-importmap-plugin",
resolveId: async (id: string, importer: string) => {
if (isValidUrl(id)) {
const url = new URL(id);
if (url.protocol === "deno:" || url.protocol === "node:") {
return { id, external: true };
}
}

try {
const resolved = generator.importMap.resolve(id, importer);
return { id: resolved };
} catch (err) {
console.warn(
`Failed to resolve ${id} from ${importer}, makring as external`
);
return { id, external: true };
}
},
load: async (id: string) => {
try {
const url = new URL(id);
if (url.protocol === "file:") {
const filePath =
path.extname(url.pathname) === ""
? `${url.pathname}.js`
: url.pathname;

return await fs.readFile(pathToFileURL(filePath), "utf-8");
}

if (url.protocol === "https:") {
const response = await fetch(id);
return await response.text();
}
} catch (err) {
throw new JspmError(
`\n Unsupported protocol ${id} \n ${err.message} \n`
);
}
},
};
};
23 changes: 21 additions & 2 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import install from "./install";
import link from "./link";
import uninstall from "./uninstall";
import update from "./update";
import { JspmError, availableProviders , wrapCommand } from "./utils";
import { JspmError, availableProviders, wrapCommand } from "./utils";
import build from "./build/index";

export const cli = cac(c.yellow("jspm"));

Expand All @@ -44,7 +45,9 @@ const resolutionOpt: opt = [
];
const providerOpt: opt = [
"-p, --provider <provider>",
`Default module provider. Available providers: ${availableProviders.join(", ")}`,
`Default module provider. Available providers: ${availableProviders.join(
", "
)}`,
{},
];
const stdoutOpt: opt = [
Expand Down Expand Up @@ -88,6 +91,16 @@ const freezeOpt: opt = [
{ default: false },
];
const silentOpt: opt = ["--silent", "Silence all output", { default: false }];
const buildConfigOpt: opt = [
"--config <file>",
"Path to a rollup config file",
{},
];
const buildOutputOpt: opt = [
"--output <dir>",
"Path to the rollup output directory",
{},
];

cli
.option(...silentOpt)
Expand Down Expand Up @@ -278,6 +291,12 @@ Clears the global module fetch cache, for situations where the contents of a dep
.alias("cc")
.action(wrapCommand(clearCache));

cli
.command("build [entry]", "Build the module using importmap")
.option(...buildConfigOpt)
.option(...buildOutputOpt)
.action(wrapCommand(build));

// Taken from 'cac', as they don't export it:
interface HelpSection {
title?: string;
Expand Down
Loading

0 comments on commit d77f468

Please sign in to comment.