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(compiler): compiler returns import locations #139

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
3 changes: 2 additions & 1 deletion packages/lwc-compiler/src/__tests__/compiler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,12 @@ describe("compiler output", () => {
test("should return output object with expected properties", async () => {
const output = await compile(VALID_CONFIG);
const { success, diagnostics, result } = output;
const { code, references } = result;
const { code, references, metadata } = result;

expect(code).toBeDefined();
expect(diagnostics).toBeDefined();
expect(references).toBeDefined();
expect(metadata).toBeDefined();
expect(success).toBeDefined();
});

Expand Down
36 changes: 19 additions & 17 deletions packages/lwc-compiler/src/__tests__/fixtures.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ describe("compilation mode", () => {

expect(metadata).toEqual({
decorators: [],
references: [{ name: "engine", type: "module" }]
references: [{ name: "engine", type: "module" }],
importLocations: [
{ location: { length: 8, start: 31 }, name: '"engine"' }
]
});
});

Expand All @@ -91,15 +94,14 @@ describe("compilation mode", () => {
...{ outputConfig: { compat: true } }
};
const { result: { code, metadata } } = await compile(config);

expect(pretify(code)).toBe(
pretify(readFixture("expected-compat-mode.js"))
);

expect(metadata).toMatchObject({
decorators: [],
references: [{ name: "engine", type: "module" }]
});
const { decorators, references, importLocations } = metadata;
expect(references).toMatchObject([{ name: "engine", type: "module" }]);
expect(decorators.length).toBe(0);
expect(importLocations.length).toBe(8);
});

it("handles prod-compat mode", async () => {
Expand All @@ -113,29 +115,28 @@ describe("compilation mode", () => {
pretify(readFixture("expected-prod_compat-mode.js"))
);

expect(metadata).toEqual({
decorators: [],
references: [{ name: "engine", type: "module" }]
});
const { decorators, references, importLocations } = metadata;
expect(references).toMatchObject([{ name: "engine", type: "module" }]);
expect(decorators.length).toBe(0);
expect(importLocations.length).toBe(8);
});
});

describe("node env", function() {
it('sets env.NODE_ENV to "development" by default', async () => {
const config = {
name: 'foo',
namespace: 'x',
name: "foo",
namespace: "x",
files: {
'foo.js': 'export const env = process.env.NODE_ENV'
"foo.js": "export const env = process.env.NODE_ENV"
},
outputConfig: { format: "es" }
};
const { result: { code, metadata }} = await compile(config);
const { result: { code, metadata } } = await compile(config);

expect(pretify(code)).toBe(
'const env = "development";\nexport { env };',
'const env = "development";\nexport { env };'
);

});

it("removes production code when NODE_ENV option is production", async () => {
Expand Down Expand Up @@ -216,7 +217,8 @@ describe("metadata output", () => {
{ name: "engine", type: "module" },
{ name: "todo", type: "module" },
{ name: "@schema/foo.bar", type: "module" }
]
],
importLocations: []
});
});
});
2 changes: 1 addition & 1 deletion packages/lwc-compiler/src/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe("test index entry points", () => {
expect(code).toBe("debugger;");
});

test.only("able to retrieve version", () => {
test("able to retrieve version", () => {
expect(version).toBeDefined();
});
});
30 changes: 30 additions & 0 deletions packages/lwc-compiler/src/bundler/__tests__/bundler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,34 @@ describe('bundler', () => {
expect(diagnostics).toBeDefined();
expect(metadata).toBeDefined();
});

test("import location is returned for valid bundle", async () => {
const src = `
import xBar from 'x-bar';
import xFoo from 'x-foo';
import xZoo from 'x-zoo';
xBoo();
xFoo();
xZoo();
`;

const config = {
outputConfig: {
env: { NODE_ENV: "development" },
minify: false,
compat: false
},
name: "foo",
namespace: "x",
files: { "foo.js": src }
};

const { metadata: {importLocations} } = await bundle(config);
expect(importLocations.length).toBe(3);
expect(importLocations[0].name).toBe("x-bar");
expect(importLocations[0].location).toMatchObject({
start: 18,
length: 5
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { collectImportLocations } from "../../bundler/import-location-collector";

describe("import locations", () => {
test("location collector should return empty array if incoming code isn't module", () => {
const locs = collectImportLocations("debugger");
expect(locs.length).toBe(0);
});

test("location collector should return empty array if no imports were specified", () => {
const src = `define('x-foo', function () {});`
const locs = collectImportLocations("debugger");
expect(locs.length).toBe(0);
});

test("location collector should return location object for each import", () => {
const src = `define('x-foo', ['x-bar', '@xfoose', 'xy/zoolaf'], function (xBar, xFoose, xZoolaf) {
xBoo();
xFoose();
xZoolaf();
});`;
const locs = collectImportLocations(src);

expect(locs.length).toBe(3);
expect(locs[0]).toMatchObject({
name: "x-bar",
location: {
start: 18,
length: 5
}
});
expect(locs[1]).toMatchObject({
name: "@xfoose",
location: {
start: 27,
length: 7
}
});
expect(locs[2]).toMatchObject({
name: "xy/zoolaf",
location: {
start: 38,
length: 9
}
});
});
});
10 changes: 9 additions & 1 deletion packages/lwc-compiler/src/bundler/bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export interface BundleReport {
metadata: BundleMetadata;
}

import {
collectImportLocations
} from "./import-location-collector";

interface RollupWarning {
message: string;
frame?: string;
Expand Down Expand Up @@ -64,7 +68,7 @@ export async function bundle(
}),
rollupModuleResolver({
metadataCollector,
options,
options
}),
rollupTransform({
metadataCollector,
Expand Down Expand Up @@ -93,6 +97,10 @@ export async function bundle(
format
});

metadataCollector.collectImportLocations(
collectImportLocations(code) || []
);

return {
diagnostics,
code,
Expand Down
43 changes: 43 additions & 0 deletions packages/lwc-compiler/src/bundler/import-location-collector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Location } from "../common-interfaces/location";

export interface ModuleImportLocation {
name: string;
location: Location;
}

const MODULE_IMPORT_REGEX = /(?:define\([(['|"][\w-]+['|"],?\s*)(?:\[((?:['|"][@\w-/]+['|"],?\s*)+)\])?,?\s*function/;

export function collectImportLocations(code: string) {
const locations: ModuleImportLocation[] = [];
const matches = new RegExp(MODULE_IMPORT_REGEX).exec(code);

// assert amd
if (!matches || !matches.length) {
return locations;
}

const searchSubstring: string = matches[0];

// format: `'x-bar', 'x-foo'`
const rawImports = matches[1];
if (!rawImports) {
return locations;
}

// split result: ["'x-bar', 'x-foo'"]
const imports = rawImports.split(/,\s*/) || [];

imports.forEach(moduleImport => {
const normalizedName = moduleImport.replace(/'/g, "");
const position = searchSubstring.indexOf(normalizedName);

locations.push({
name: normalizedName,
location: {
start: position,
length: normalizedName.length
}
});
});
return locations;
}
11 changes: 10 additions & 1 deletion packages/lwc-compiler/src/bundler/meta-collector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import {
WireDecorator
} from "babel-plugin-transform-lwc-class";

import { ModuleImportLocation } from "./import-location-collector";

export type MetadataDecorators = Array<
ApiDecorator | TrackDecorator | WireDecorator
>;

export interface BundleMetadata {
references: ExternalReference[];
decorators: MetadataDecorators;
importLocations: ModuleImportLocation[];
}

export interface ExternalReference {
Expand All @@ -23,6 +26,7 @@ export class MetadataCollector {
private decorators: Array<
ApiDecorator | TrackDecorator | WireDecorator
> = [];
private importLocations: ModuleImportLocation[] = [];

public collectReference(reference: ExternalReference) {
const existingRef = this.references.get(reference.name);
Expand All @@ -39,10 +43,15 @@ export class MetadataCollector {
this.decorators.push(decorator);
}

public collectImportLocations(importLocations: ModuleImportLocation[]) {
this.importLocations.push(...importLocations);
}

public getMetadata(): BundleMetadata {
return {
references: Array.from(this.references.values()),
decorators: this.decorators
decorators: this.decorators,
importLocations: this.importLocations
};
}
}
10 changes: 6 additions & 4 deletions packages/lwc-compiler/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ export async function compile(
diagnostics.push(...bundleReport.diagnostics);

if (!hasError(diagnostics)) {
const { diagnostics: bundleDiagnostics , code, metadata } = await bundle(
normalizedOptions
);
const {
diagnostics: bundleDiagnostics,
code,
metadata,
} = await bundle(normalizedOptions);

diagnostics.push(...bundleDiagnostics);

Expand All @@ -44,7 +46,7 @@ export async function compile(
code,
map: null,
metadata,
references: bundleReport.references
references: bundleReport.references,
};
}
}
Expand Down