-
Notifications
You must be signed in to change notification settings - Fork 63
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
fix: ability to load local files through cli option "require" #797
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import fs from "fs"; | ||
|
||
export const exists = async (path: string): Promise<boolean> => { | ||
try { | ||
await fs.promises.access(path); | ||
return true; | ||
} catch (_) { | ||
return false; | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import path from "path"; | ||
import { exists } from "./fs"; | ||
|
||
export const requireModule = async <T = unknown>(modulePath: string): Promise<T> => { | ||
const isModuleLocal = await exists(modulePath); | ||
|
||
return require(isModuleLocal ? path.resolve(modulePath) : modulePath); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ const Promise = require("bluebird"); | |
const debug = require("debug")(`hermione:worker:${process.pid}`); | ||
const ipc = require("../utils/ipc"); | ||
const { MASTER_INIT, MASTER_SYNC_CONFIG, WORKER_INIT, WORKER_SYNC_CONFIG } = require("../constants/process-messages"); | ||
const { requireModule } = require("../utils/module"); | ||
|
||
module.exports = class HermioneFacade { | ||
static create() { | ||
|
@@ -45,15 +46,21 @@ module.exports = class HermioneFacade { | |
|
||
ipc.on(MASTER_INIT, ({ configPath, runtimeConfig } = {}) => { | ||
try { | ||
const promise = Promise.resolve(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Подписка на событие - не асинхронная операция, поэтому реализовал через докидывание промисов. |
||
|
||
if (runtimeConfig.requireModules) { | ||
runtimeConfig.requireModules.forEach(module => require(module)); | ||
runtimeConfig.requireModules.forEach(modulePath => { | ||
promise.then(requireModule(modulePath)); | ||
}); | ||
} | ||
|
||
RuntimeConfig.getInstance().extend(runtimeConfig); | ||
const hermione = Hermione.create(configPath); | ||
|
||
debug("worker initialized"); | ||
resolve(hermione); | ||
promise.then(() => { | ||
debug("worker initialized"); | ||
resolve(hermione); | ||
}); | ||
} catch (e) { | ||
debug("worker initialization failed"); | ||
reject(e); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import fs from "fs"; | ||
import sinon, { SinonStub } from "sinon"; | ||
import { exists } from "../../../src/utils/fs"; | ||
|
||
describe("utils/fs", () => { | ||
const sandbox = sinon.createSandbox(); | ||
|
||
beforeEach(() => { | ||
sandbox.stub(fs.promises, "access"); | ||
}); | ||
|
||
afterEach(() => sandbox.restore()); | ||
|
||
describe("exists", () => { | ||
it("should return 'true' if file exists", async () => { | ||
const path = "./some-path.js"; | ||
(fs.promises.access as SinonStub).withArgs(path).resolves(); | ||
|
||
const isExists = await exists(path); | ||
|
||
assert.isTrue(isExists); | ||
}); | ||
|
||
it("should return 'false' if file doesn't exists", async () => { | ||
const path = "./some-path.js"; | ||
(fs.promises.access as SinonStub).withArgs(path).rejects(new Error("o.O")); | ||
|
||
const isExists = await exists(path); | ||
|
||
assert.isFalse(isExists); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import path from "path"; | ||
import sinon, { SinonStub } from "sinon"; | ||
import proxyquire from "proxyquire"; | ||
import { requireModule as realRequireModule } from "../../../src/utils/module"; | ||
|
||
describe("utils/module", () => { | ||
let isNodeModuleRequired: SinonStub; | ||
let isLocalModuleRequired: SinonStub; | ||
let exists: SinonStub; | ||
let requireModule: typeof realRequireModule; | ||
|
||
const sandbox = sinon.createSandbox(); | ||
const nodeModulePath = "foo-module"; | ||
const relativeLocalModulePath = "./bar-module"; | ||
const absoluteLocalModulePath = path.resolve(relativeLocalModulePath); | ||
|
||
beforeEach(() => { | ||
isNodeModuleRequired = sinon.stub(); | ||
isLocalModuleRequired = sinon.stub(); | ||
exists = sinon.stub(); | ||
|
||
({ requireModule } = proxyquire.noCallThru().load("../../../src/utils/module", { | ||
"./fs": { exists }, | ||
[nodeModulePath]: isNodeModuleRequired, | ||
[absoluteLocalModulePath]: isLocalModuleRequired, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Не придумал как это по другому протестить. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. давай не городить такое. здесь минимальная фикстура явно будет смотреться в разы лучше и проще There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Что имеешь ввиду под минимальной фикстурой? У меня это вроде бы тоже минимальная. Т.е. проверяю правильный There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ну ты здесь всё застабал и от реальной реализации осталась только структура твоих модулей. Я вообще сомневаюсь, что этот тест что-то отловит, если ты что-то в логике поменяешь :) Я имел в виду то, что вместо того, чтобы стабать реквайр, можно его выполнить по-настоящему. Да и модуль можно в виде пустышки положить, чтобы его зареквайрить вживую There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
ну так это же unit-тесты. Я в них застабал все лишнее. И проверяю только логику самой функции. Т.е. корректный реквайр модуля из
Если логику изменить, то тест упадет. Я это проверял при написании.
Ну по факту я не стабаю Про создание модуля на fs и его require я тоже думал. Но непонятно как в этом кейсе выполнить ассерт и убедится, что было зареквайрено то что нужно. Поэтому использовал текущий подход. |
||
})); | ||
}); | ||
|
||
afterEach(() => sandbox.restore()); | ||
|
||
describe("requireModule", () => { | ||
it("should require module from node-modules", async () => { | ||
exists.withArgs(nodeModulePath).resolves(false); | ||
const module = await requireModule<SinonStub>(nodeModulePath); | ||
|
||
module(); | ||
|
||
assert.calledOnce(isNodeModuleRequired); | ||
assert.notCalled(isLocalModuleRequired); | ||
}); | ||
|
||
it("should require module from node-modules", async () => { | ||
exists.withArgs(relativeLocalModulePath).resolves(true); | ||
const module = await requireModule<SinonStub>(relativeLocalModulePath); | ||
|
||
module(); | ||
|
||
assert.calledOnce(isLocalModuleRequired); | ||
assert.notCalled(isNodeModuleRequired); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Если переданный путь существует на
fs
, то с помощьюpath.resolve
привожу к абсолютному пути и реквайрю. Такой подход подсмотрел вmocha