Skip to content

Commit

Permalink
feature(extract): adds basic support for ESM urls
Browse files Browse the repository at this point in the history
  • Loading branch information
sverweij committed Apr 24, 2021
1 parent 201ae05 commit 5f70596
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 3 deletions.
6 changes: 5 additions & 1 deletion src/extract/get-dependencies.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const toJavascriptAST = require("./parse/to-javascript-ast");
const toTypescriptAST = require("./parse/to-typescript-ast");
const toSwcAST = require("./parse/to-swc-ast");
const detectPreCompilationNess = require("./utl/detect-pre-compilation-ness");
const extractModuleAttributes = require("./utl/extract-module-attributes");

function extractFromSwcAST(pOptions, pFileName) {
return extractSwcDeps(
Expand Down Expand Up @@ -101,7 +102,10 @@ function extractDependencies(pCruiseOptions, pFileName, pTranspileOptions) {
);
}

return lDependencies;
return lDependencies.map((pDependency) => ({
...pDependency,
...extractModuleAttributes(pDependency.module),
}));
}

function matchesDoNotFollow(pResolved, pDoNotFollow) {
Expand Down
37 changes: 37 additions & 0 deletions src/extract/utl/extract-module-attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* eslint-disable security/detect-object-injection */

/**
* Given a module string returns in an object
* - the module name
* - the protocol (when encoded in the string)
* - the mimeType (when encoded in the string)
*
* See https://nodejs.org/api/esm.html#esm_urls
*
* would've loved to use url.URL here, but that doesn't extract the mime type
* (if there's a default node API that does this I'm all ears)
*
* @param {string} pString
* @returns {any}
*/
module.exports = function extractModuleAttributes(pString) {
let lReturnValue = { module: pString };
const lModuleAttributes = pString.match(
// eslint-disable-next-line security/detect-unsafe-regex, unicorn/no-unsafe-regex
/^(node:|file:|data:)?(([^,]+),)?(.+)$/
);
const lProtocolPosition = 1;
const lMimeTypePosition = 3;
const lModulePosition = 4;

if (lModuleAttributes) {
lReturnValue.module = lModuleAttributes[lModulePosition];
if (lModuleAttributes[lProtocolPosition]) {
lReturnValue.protocol = lModuleAttributes[lProtocolPosition];
}
if (lModuleAttributes[lMimeTypePosition]) {
lReturnValue.mimeType = lModuleAttributes[lMimeTypePosition];
}
}
return lReturnValue;
};
8 changes: 8 additions & 0 deletions src/schema/cruise-result.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,14 @@
"type": "string",
"description": "The name of the module as it appeared in the source code, e.g. './main'"
},
"protocol": {
"type": "string",
"description": "If the module specification is an URI with a protocol in it (e.g. `import * as fs from 'node:fs'` or `import stuff from 'data:application/json,some-thing'`) - this attribute holds the protocol part (e.g. 'node', 'data', 'file')"
},
"mimeType": {
"type": "string",
"description": "If the module specification is an URI and contains a mime type, this attribute holds the mime type (e.g. in `import stuff from 'data:application/json,some-thing'`this would be data:application/json"
},
"resolved": {
"type": "string",
"description": "The (resolved) file name of the module, e.g. 'src/main/index.js'"
Expand Down
55 changes: 55 additions & 0 deletions test/extract/utl/extract-module-attributes.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const expect = require("chai").expect;
const extractModuleAttributes = require("../../../src/extract/utl/extract-module-attributes");

describe("extract/utl/extract-module-attributes", () => {
it("leaves regular module specifications alone", () => {
expect(extractModuleAttributes("protodash")).to.deep.equal({
module: "protodash",
});
});

it("extracts the protocol if there is one", () => {
expect(extractModuleAttributes("node:fs")).to.deep.equal({
module: "fs",
protocol: "node:",
});
});

it("leaves things alone the protocol is unknown", () => {
expect(extractModuleAttributes("nod:fs")).to.deep.equal({
module: "nod:fs",
});
});

it("manages empty strings gracefully", () => {
expect(extractModuleAttributes("")).to.deep.equal({
module: "",
});
});

it("extracts both protocol and mimeType when they're in the URI", () => {
expect(
extractModuleAttributes("data:application/json,gegevens.json")
).to.deep.equal({
module: "gegevens.json",
protocol: "data:",
mimeType: "application/json",
});
});

it("handles emtpy mimeTypes gracefulley", () => {
expect(extractModuleAttributes("data:,gegevens.json")).to.deep.equal({
module: ",gegevens.json",
protocol: "data:",
});
});

it("when protocol separator is mistyped, returns it as part of the module name", () => {
expect(
extractModuleAttributes("data:application/json;gegevens.json")
).to.deep.equal({
module: "application/json;gegevens.json",
protocol: "data:",
});
});
});
3 changes: 2 additions & 1 deletion tools/extract-line-coverage-from-istanbul-json.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { EOL } from "os";
// eslint-disable-next-line node/no-missing-import, import/no-unresolved
import { EOL } from "node:os";
import getStream from "get-stream";

const DECIMAL_BASE = 10;
Expand Down
3 changes: 2 additions & 1 deletion tools/generate-schemas.utl.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fs from "fs";
// eslint-disable-next-line node/no-missing-import, import/no-unresolved
import fs from "node:fs";
import prettier from "prettier";

function jsonTheSchema(pOutputFileName) {
Expand Down
16 changes: 16 additions & 0 deletions tools/schema/dependencies.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ export default {
description:
"The name of the module as it appeared in the source code, e.g. './main'",
},
protocol: {
type: "string",
description:
"If the module specification is an URI with a protocol in it (e.g. " +
"`import * as fs from 'node:fs'` or " +
"`import stuff from 'data:application/json,some-thing'`) - this attribute " +
"holds the protocol part (e.g. 'node:', 'data:', 'file:'). Also see " +
"https://nodejs.org/api/esm.html#esm_urls",
},
mimeType: {
type: "string",
description:
"If the module specification is an URI and contains a mime type, this " +
"attribute holds the mime type (e.g. in `import stuff from 'data:application/json,some-thing'` " +
"this would be data:application/json). Also see https://nodejs.org/api/esm.html#esm_urls",
},
resolved: {
type: "string",
description:
Expand Down
17 changes: 17 additions & 0 deletions types/cruise-result.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,23 @@ export interface IDependency {
* The name of the module as it appeared in the source code, e.g. './main'
*/
module: string;
/**
* If the module specification is an URI with a protocol in it (e.g.
* `import * as fs from 'node:fs'` or
* `import stuff from 'data:application/json,some-thing'`) -
* this attribute holds the protocol part (e.g. 'node:', 'data:', 'file:').
*
* Also see https://nodejs.org/api/esm.html#esm_urls
*/
protocol: string;
/**
* If the module specification is an URI and contains a mime type, this
* attribute holds the mime type (e.g. in `import stuff from 'data:application/json,some-thing'
* `this would be data:application/json)
*
* Also see https://nodejs.org/api/esm.html#esm_urls
*/
mimeType: string;
moduleSystem: ModuleSystemType;
/**
* The (resolved) file name of the module, e.g. 'src/main//index.js'
Expand Down

0 comments on commit 5f70596

Please sign in to comment.