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

(feat): Add build command by wrapping rollup #2544

Merged
merged 10 commits into from
Oct 24, 2023
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'
JayaKrishnaNamburu marked this conversation as resolved.
Show resolved Hide resolved

[[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:[email protected]

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:[email protected]` 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:[email protected]/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:[email protected]

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:[email protected]` 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:[email protected]/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 });
}
65 changes: 65 additions & 0 deletions src/build/rollup-importmap-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
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 };
JayaKrishnaNamburu marked this conversation as resolved.
Show resolved Hide resolved
} catch (err) {
return { id, external: true };
JayaKrishnaNamburu marked this conversation as resolved.
Show resolved Hide resolved
}
},
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;
JayaKrishnaNamburu marked this conversation as resolved.
Show resolved Hide resolved

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
Loading