Skip to content

Commit

Permalink
Fix addSource to allow implementation path to point to a file
Browse files Browse the repository at this point in the history
  • Loading branch information
ExE-Boss committed Apr 29, 2020
1 parent f7520b3 commit 9bdfffb
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 15 deletions.
54 changes: 39 additions & 15 deletions lib/transformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,39 +26,62 @@ class Transformer {
}
});

this.sources = []; // Absolute paths to the IDL and Impl directories.
this._sources = []; // Absolute paths to the IDL and Impl files or directories.
this.utilPath = null;
}

addSource(idl, impl) {
if (typeof idl !== "string") {
throw new TypeError("idl path has to be a string");
}

if (typeof impl !== "string") {
throw new TypeError("impl path has to be a string");
}
this.sources.push({ idlPath: path.resolve(idl), impl: path.resolve(impl) });

this._sources.push({
idlPath: path.resolve(idl),
implPath: path.resolve(impl)
});

return this;
}

async _collectSources() {
const stats = await Promise.all(this.sources.map(src => fs.stat(src.idlPath)));
const { implSuffix } = this.ctx;
const stats = await Promise.all(this._sources.map(src => Promise.all([
fs.stat(src.idlPath),
fs.stat(src.implPath)
])));

const files = [];
for (let i = 0; i < stats.length; ++i) {
if (stats[i].isDirectory()) {
const folderContents = await fs.readdir(this.sources[i].idlPath);
const { idlPath, implPath } = this._sources[i];
const [idlStat, implStat] = stats[i];

if (idlStat.isDirectory()) {
if (!implStat.isDirectory()) {
throw new Error("Internal error: When the idl path is a directory, the impl path must also be a directory.");
}

const folderContents = await fs.readdir(this._sources[i].idlPath);
for (const file of folderContents) {
if (file.endsWith(".webidl")) {
if (path.extname(file) === ".webidl") {
files.push({
idlPath: path.join(this.sources[i].idlPath, file),
impl: this.sources[i].impl
idlPath: path.join(idlPath, file),
implPath: path.join(implPath, `${path.basename(file, ".webidl")}${implSuffix}.js`)
});
}
}
} else if (implStat.isDirectory()) {
files.push({
idlPath,
implPath: path.join(implPath, `${path.basename(idlPath, ".webidl")}${implSuffix}.js`)
});
} else {
files.push({
idlPath: this.sources[i].idlPath,
impl: this.sources[i].impl
idlPath,
implPath
});
}
}
Expand All @@ -71,7 +94,7 @@ class Transformer {
for (let i = 0; i < files.length; ++i) {
zipped.push({
idlContent: fileContents[i],
impl: files[i].impl
implPath: files[i].implPath
});
}
return zipped;
Expand All @@ -80,7 +103,7 @@ class Transformer {
_parse(outputDir, contents) {
const parsed = contents.map(content => ({
idl: webidl.parse(content.idlContent),
impl: content.impl
impl: content.implPath
}));

this.ctx.initialize();
Expand All @@ -97,7 +120,7 @@ class Transformer {
}

obj = new Interface(this.ctx, instruction, {
implDir: file.impl
implFile: file.impl
});
interfaces.set(obj.name, obj);
break;
Expand Down Expand Up @@ -208,7 +231,7 @@ class Transformer {
for (const obj of interfaces.values()) {
let source = obj.toString();

let implFile = path.relative(outputDir, path.resolve(obj.opts.implDir, obj.name + this.ctx.implSuffix));
let implFile = path.relative(outputDir, obj.opts.implFile);
implFile = implFile.replace(/\\/g, "/"); // fix windows file paths
if (implFile[0] !== ".") {
implFile = "./" + implFile;
Expand All @@ -220,7 +243,7 @@ class Transformer {
const conversions = require("webidl-conversions");
const utils = require("${relativeUtils}");
${source}
const Impl = require("${implFile}.js");
const Impl = require("${implFile}");
`;

source = this._prettify(source);
Expand Down Expand Up @@ -250,6 +273,7 @@ class Transformer {
${obj.toString()}
`);

await fs.writeFile(path.join(outputDir, obj.name + ".js"), source);
}
}
Expand Down
109 changes: 109 additions & 0 deletions test/__snapshots__/test.js.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,114 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Transformer API addSource implementation file custom path 1`] = `
"\\"use strict\\";

const conversions = require(\\"webidl-conversions\\");
const utils = require(\\"./utils.js\\");

const implSymbol = utils.implSymbol;
const ctorRegistrySymbol = utils.ctorRegistrySymbol;

const interfaceName = \\"Foo\\";

exports.is = value => {
return utils.isObject(value) && utils.hasOwn(value, implSymbol) && value[implSymbol] instanceof Impl.implementation;
};
exports.isImpl = value => {
return utils.isObject(value) && value instanceof Impl.implementation;
};
exports.convert = (value, { context = \\"The provided value\\" } = {}) => {
if (exports.is(value)) {
return utils.implForWrapper(value);
}
throw new TypeError(\`\${context} is not of type 'Foo'.\`);
};

function makeWrapper(globalObject) {
if (globalObject[ctorRegistrySymbol] === undefined) {
throw new Error(\\"Internal error: invalid global object\\");
}

const ctor = globalObject[ctorRegistrySymbol][\\"Foo\\"];
if (ctor === undefined) {
throw new Error(\\"Internal error: constructor Foo is not installed on the passed global object\\");
}

return Object.create(ctor.prototype);
}

exports.create = (globalObject, constructorArgs, privateData) => {
const wrapper = makeWrapper(globalObject);
return exports.setup(wrapper, globalObject, constructorArgs, privateData);
};

exports.createImpl = (globalObject, constructorArgs, privateData) => {
const wrapper = exports.create(globalObject, constructorArgs, privateData);
return utils.implForWrapper(wrapper);
};

exports._internalSetup = (wrapper, globalObject) => {};

exports.setup = (wrapper, globalObject, constructorArgs = [], privateData = {}) => {
privateData.wrapper = wrapper;

exports._internalSetup(wrapper, globalObject);
Object.defineProperty(wrapper, implSymbol, {
value: new Impl.implementation(globalObject, constructorArgs, privateData),
configurable: true
});

wrapper[implSymbol][utils.wrapperSymbol] = wrapper;
if (Impl.init) {
Impl.init(wrapper[implSymbol]);
}
return wrapper;
};

exports.new = globalObject => {
const wrapper = makeWrapper(globalObject);

exports._internalSetup(wrapper, globalObject);
Object.defineProperty(wrapper, implSymbol, {
value: Object.create(Impl.implementation.prototype),
configurable: true
});

wrapper[implSymbol][utils.wrapperSymbol] = wrapper;
if (Impl.init) {
Impl.init(wrapper[implSymbol]);
}
return wrapper[implSymbol];
};

const exposed = new Set([\\"Window\\"]);

exports.install = (globalObject, globalNames) => {
if (!globalNames.some(globalName => exposed.has(globalName))) {
return;
}
class Foo {
constructor() {
throw new TypeError(\\"Illegal constructor\\");
}
}
Object.defineProperties(Foo.prototype, { [Symbol.toStringTag]: { value: \\"Foo\\", configurable: true } });
if (globalObject[ctorRegistrySymbol] === undefined) {
globalObject[ctorRegistrySymbol] = Object.create(null);
}
globalObject[ctorRegistrySymbol][interfaceName] = Foo;

Object.defineProperty(globalObject, interfaceName, {
configurable: true,
writable: true,
value: Foo
});
};

const Impl = require(\\"../fixtures/custom-path/impl-file.js\\");
"
`;

exports[`with processors AsyncCallbackInterface.webidl 1`] = `
"\\"use strict\\";

Expand Down
9 changes: 9 additions & 0 deletions test/fixtures/custom-path/impl-file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use strict";

exports.implementation = class FooImpl {
constructor(globalObject, args, privateData) {
this._globalObject = globalObject;
this._args = args;
this._privateData = privateData;
}
};
3 changes: 3 additions & 0 deletions test/fixtures/custom-path/some.webidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[Exposed=Window]
interface Foo {
};
22 changes: 22 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,31 @@ const rootDir = path.resolve(__dirname, "..");
const casesDir = path.resolve(__dirname, "cases");
const implsDir = path.resolve(__dirname, "implementations");
const outputDir = path.resolve(__dirname, "output");
const fixturesDir = path.resolve(__dirname, "fixtures");

const idlFiles = fs.readdirSync(casesDir);

describe("Transformer API", () => {
describe("addSource", () => {
test("implementation file custom path", async () => {
const customPathDir = path.resolve(fixturesDir, "custom-path");

const transformer = new Transformer({ implSuffix: "-impl" });
transformer.addSource(
path.join(customPathDir, "some.webidl"),
path.join(customPathDir, "impl-file.js")
);

await transformer.generate(outputDir);

const outputFile = path.resolve(outputDir, "Foo.js");
const output = fs.readFileSync(outputFile, { encoding: "utf-8" });

expect(output).toMatchSnapshot();
});
});
});

describe("without processors", () => {
beforeAll(() => {
const transformer = new Transformer();
Expand Down

0 comments on commit 9bdfffb

Please sign in to comment.