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 integrity flag support #2571

Merged
merged 7 commits into from
Jul 14, 2024
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: [14.x, 16.x, 18.x]
node: [16.x, 18.x, 20.x, 22.x]
steps:
- uses: actions/checkout@v2
with:
Expand Down
8 changes: 4 additions & 4 deletions docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ If no modules are given, all "imports" in the initial map are relinked.
* `--cache` _<mode>_ Cache mode for fetches (online, offline, no-cache) (default: online)
* `--root` _<url>_ URL to treat as server root, i.e. rebase import maps against
* `--preload` _[mode]_ Add module preloads to HTML output (default: static, dynamic)
* `--integrity` Add module preloads with integrity attributes to HTML output (default: false)
* `--integrity` Add module integrity attributes to the import map (default: false)
* `--compact` Output a compact import map (default: false)
* `--stdout` Output the import map to stdout (default: false)
* `--silent` Silence all output (default: false)
Expand Down Expand Up @@ -87,7 +87,7 @@ If no packages are provided, all "imports" in the initial map are reinstalled.
* `--cache` _<mode>_ Cache mode for fetches (online, offline, no-cache) (default: online)
* `--root` _<url>_ URL to treat as server root, i.e. rebase import maps against
* `--preload` _[mode]_ Add module preloads to HTML output (default: static, dynamic)
* `--integrity` Add module preloads with integrity attributes to HTML output (default: false)
* `--integrity` Add module integrity attributes to the import map (default: false)
* `--compact` Output a compact import map (default: false)
* `--stdout` Output the import map to stdout (default: false)
* `--silent` Silence all output (default: false)
Expand Down Expand Up @@ -137,7 +137,7 @@ Uninstalls packages from an import map. The given packages must be valid package
* `--cache` _<mode>_ Cache mode for fetches (online, offline, no-cache) (default: online)
* `--root` _<url>_ URL to treat as server root, i.e. rebase import maps against
* `--preload` _[mode]_ Add module preloads to HTML output (default: static, dynamic)
* `--integrity` Add module preloads with integrity attributes to HTML output (default: false)
* `--integrity` Add module integrity attributes to the import map (default: false)
* `--compact` Output a compact import map (default: false)
* `--stdout` Output the import map to stdout (default: false)
* `--silent` Silence all output (default: false)
Expand Down Expand Up @@ -169,7 +169,7 @@ Updates packages in an import map to the latest versions that are compatible wit
* `--cache` _<mode>_ Cache mode for fetches (online, offline, no-cache) (default: online)
* `--root` _<url>_ URL to treat as server root, i.e. rebase import maps against
* `--preload` _[mode]_ Add module preloads to HTML output (default: static, dynamic)
* `--integrity` Add module preloads with integrity attributes to HTML output (default: false)
* `--integrity` Add module integrity attributes to the import map (default: false)
* `--compact` Output a compact import map (default: false)
* `--stdout` Output the import map to stdout (default: false)
* `--silent` Silence all output (default: false)
Expand Down
4,720 changes: 2,417 additions & 2,303 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,22 @@
"jspm.js"
],
"dependencies": {
"@jspm/generator": "^2.0.0",
"@jspm/generator": "^2.1.2",
"cac": "^6.7.14",
"ora": "^6.3.0",
"picocolors": "^1.0.0",
"rollup": "^3.29.2"
},
"devDependencies": {
"@antfu/eslint-config": "^0.34.2",
"@babel/core": "^7.21.4",
"@babel/core": "^7.24.7",
"@types/node": "^18.15.11",
"esbuild": "^0.16.17",
"eslint": "^8.38.0",
"eslint-config-prettier": "^8.8.0",
"prettier": "^2.8.7",
"tinyspy": "^1.1.1",
"tsx": "^3.12.6",
"tsx": "^4.16.2",
"typescript": "^4.9.5"
}
}
3 changes: 2 additions & 1 deletion src/build/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from "node:path";
import process from "node:process";
import { pathToFileURL } from "node:url";
import { type RollupOptions, rollup } from "rollup";

import { JspmError, exists } from "../utils";
Expand Down Expand Up @@ -40,7 +41,7 @@ export default async function build(entry: string, options: Flags) {
`Build config file does not exist: ${buildConfigPath}`
);
}
const rollupConfig = await import(buildConfigPath)
const rollupConfig = await import(pathToFileURL(buildConfigPath).href)
.then((mod) => mod.default)
.catch((err) => {
throw new JspmError(`Failed to load build config: ${err}`);
Expand Down
25 changes: 11 additions & 14 deletions src/build/rollup-importmap-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,25 +44,22 @@ export const RollupImportmapPlugin = async (flags: Flags): Promise<Plugin> => {
}
},
load: async (id: string) => {
let url: URL;
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");
}
url = new URL(id);
} catch (e) {
throw new JspmError(`Unsupported URL ${id} \n ${e.message}`);
}

if (url.protocol === "https:") {
switch (url.protocol) {
case 'file:':
return await fs.readFile(new URL(id), "utf-8");
case 'https:': {
const response = await fetch(id);
return await response.text();
}
} catch (err) {
throw new JspmError(
`\n Unsupported protocol ${id} \n ${err.message} \n`
);
default:
throw new JspmError(`Unsupported protocol ${url.protocol}`);
}
},
};
Expand Down
2 changes: 1 addition & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const preloadOpt: opt = [
];
const integrityOpt: opt = [
"--integrity",
"Add module preloads with integrity attributes to HTML output",
"Add module integrity attributes to the import map",
{ default: false },
];
const cacheOpt: opt = [
Expand Down
2 changes: 1 addition & 1 deletion src/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ async function handleLocalFile(
generator: Generator
) {
const source = await fs.readFile(resolvedModule.target, { encoding: "utf8" });
const { default: babel } = await import("@babel/core");
const babel = await import("@babel/core");

try {
babel.parse(source);
Expand Down
1 change: 1 addition & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ export async function getGenerator(
defaultProvider: getProvider(flags),
resolutions: getResolutions(flags),
cache: getCacheMode(flags),
integrity: flags.integrity,
commonJS: true, // TODO: only for --local flag
});
}
Expand Down
73 changes: 73 additions & 0 deletions test/integrity.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import assert from "assert";
import {
type Scenario,
mapDirectory,
mapFile,
runScenarios,
} from "./scenarios";

const importMap = await mapFile("test/fixtures/importmap.json");

const scenarios: Scenario[] = [
// The inline improtmap should be linked and the integrity attribute should be added
{
files: importMap,
commands: ["jspm link react -o index.html --integrity"],
validationFn: async (files: Map<string, string>) => {
const html = files.get("index.html");

assert(html.includes("integrity"));
},
},
// The importmap generated should have integrity attribute
{
files: importMap,
commands: ["jspm link --integrity"],
validationFn: async (files: Map<string, string>) => {
const map = JSON.parse(files.get("importmap.json"));
assert(map.integrity);
},
},
// Scenario should detect the provider and add integrity attribute
{
files: await mapFile("test/fixtures/unpkg.importmap.json"),
commands: [
"jspm link -m unpkg.importmap.json -o importmap.json --integrity",
],
validationFn: async (files: Map<string, string>) => {
const map = JSON.parse(files.get("importmap.json"));
assert(map.integrity);
},
},
// Scenario should detect the provider and add integrity attribute
{
files: await mapDirectory("test/fixtures/scenario_provider_swap"),
commands: ["jspm install --provider nodemodules --integrity"],
validationFn: async (files) => {
const map = JSON.parse(files.get("importmap.json"));
assert(map.integrity);
},
},
// Scenario installs package from denoland along with integrity attribute
{
files: new Map(),
commands: ["jspm install denoland:zod --integrity"],
validationFn: async (files) => {
const map = JSON.parse(files.get("importmap.json"));
assert(map.imports.zod.includes("deno.land"));
assert(map.integrity);
},
},
// Scenario installs package from skypack along with integrity attribute
{
files: new Map(),
commands: ["jspm install lit --provider skypack --integrity"],
validationFn: async (files) => {
const map = JSON.parse(files.get("importmap.json"));
assert(map.imports.lit.includes("cdn.skypack.dev"));
assert(map.integrity);
},
},
];

runScenarios(scenarios);
4 changes: 2 additions & 2 deletions test/ownname.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ const scenarios: Scenario[] = [
commands: ["jspm install app"],
validationFn: async (files: Map<string, string>) => {
// Installing the own-name package "app" should result in the version of
// es-module-lexer in the import map being upgraded to 1.4.1, since it's a
// es-module-lexer in the import map being upgraded to 1.5.4, since it's a
// transitive dependency of "./app.js".
const map = JSON.parse(files.get("importmap.json"));
assert(
map?.imports?.["es-module-lexer"]?.includes("es-module-lexer@1.4.1")
map?.imports?.["es-module-lexer"]?.includes("es-module-lexer@1.5.4")
);
},
},
Expand Down
8 changes: 6 additions & 2 deletions test/providers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@ for (const provider of availableProviders) {
let spec = "lit";
let name = "lit";
if (provider.includes("deno")) {
spec = "denoland:oak/body.ts"; // deno doesn't support npm packages
name = "oak/body.ts";
// oak is using jsr. We need to add support for jsr registry and imort protocol
// https://github.com/jspm/generator/issues/366
// spec = "denoland:oak/body.ts"; // deno doesn't support npm packages
// name = "oak/body.ts";
spec = "denoland:zod";
name = "zod";
}
if (provider === "node") {
spec = "@jspm/core/nodelibs/fs"; // node provider is only for polyfills
Expand Down
9 changes: 4 additions & 5 deletions test/scenarios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export async function mapDirectory(dir: string): Promise<Files> {
} else {
const subFiles = await mapDirectory(filePath);
for (const [subFile, subData] of subFiles) {
files.set(path.join(file, subFile).replace(/\\/g, '/'), subData);
files.set(path.join(file, subFile).replace(/\\/g, "/"), subData);
}
}
}
Expand Down Expand Up @@ -103,10 +103,9 @@ async function deleteTmpPkg(dir: string) {
try {
await fs.rm(dir, { recursive: true });
return;
}
catch (err) {
if (err.code === 'EBUSY')
await new Promise(resolve => setTimeout(resolve, 10));
} catch (err) {
if (err.code === "EBUSY")
await new Promise((resolve) => setTimeout(resolve, 10));
}
}
} else {
Expand Down
Loading