Skip to content

Commit

Permalink
Merge pull request #76 from garronej/config-assertion
Browse files Browse the repository at this point in the history
test(config): improved the assertion and parsing of config
  • Loading branch information
garronej committed Oct 19, 2022
2 parents c807031 + 536a3d7 commit 391c636
Show file tree
Hide file tree
Showing 19 changed files with 512 additions and 410 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ jobs:
node: ["18", "17", "16", "14"]
os: [windows-latest, ubuntu-latest]
name: Test with Node v${{ matrix.node }} on ${{ matrix.os }}
env:
IS_CI: true
steps:
- name: Tell if project is using npm or yarn
id: step1
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "denoify",
"version": "1.2.5",
"version": "1.2.6",
"homepage": "https://denoify.land",
"description": "For NPM module authors that would like to support Deno but do not want to write and maintain a port.",
"main": "dist/lib/index.js",
Expand Down Expand Up @@ -71,6 +71,7 @@
"@types/node": "^10.0.0",
"@types/node-fetch": "^2.5.6",
"@types/url-join": "^4.0.0",
"cases-of-test": "^0.0.0",
"husky": "^4.3.8",
"js-yaml": "^3.13.1",
"lint-staged": "^11.0.0",
Expand Down
21 changes: 4 additions & 17 deletions res/notes_for_later.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,9 @@ Example with `ipaddr.js`:

```typescript
import { Version } from "../../tools/Version";
importParsedImportExportStatement } from "../types/ParsedImportExportStatement";

export async function importExportStatementReplacer(
params: {
importExportStatement: string,
version: string
}
): Promise<undefined | string> {
import { ParsedImportExportStatement } from "../types/ParsedImportExportStatement";

export async function importExportStatementReplacer(params: { importExportStatement: string; version: string }): Promise<undefined | string> {
const { importExportStatement, version } = params;

const parsedImportExportStatement = ParsedImportExportStatement.parse(importExportStatement);
Expand All @@ -23,13 +17,7 @@ export async function importExportStatementReplacer(
return undefined;
}


const typeVersion = Version.compare(
Version.parse(version),
Version.parse("1.6.0")
) <= 0 ?
"1.6.0" :
version;
const typeVersion = Version.compare(Version.parse(version), Version.parse("1.6.0")) <= 0 ? "1.6.0" : version;

return [
`// @deno-types="https://raw.githubusercontent.com/whitequark/ipaddr.js/${typeVersion}/lib/ipaddr.js.d.ts"`,
Expand All @@ -38,6 +26,5 @@ export async function importExportStatementReplacer(
"argument": `https://raw.githubusercontent.com/whitequark/ipaddr.js/${version}/lib/ipaddr.js`
})
].join("\n");

}
```
```
21 changes: 11 additions & 10 deletions src/bin/remove_deno_dist_from_gitignore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,25 @@ import * as fs from "fs";
import * as commentJson from "comment-json";
import { removeFromGitignore } from "../tools/removeFromGitignore";
import { toPosix } from "../tools/toPosix";
import { configuration } from "../lib/parseParams";
import { assert } from "tsafe/assert";
import { parseAsDenoifyConfig } from "../lib/config/parseParams";
import getFileTypeAndContent from "../lib/config/fileAndContent";

const { getDenoifyOutDir } = (() => {
async function getExplicitDenoifyOutDir(params: { moduleDirPath: string }) {
const { moduleDirPath } = params;

const config = configuration();

const denoifyOut = config.parseAsDenoifyConfig(
await config.getFileTypeAndContent(fileBasename => {
const filePath = pathJoin(moduleDirPath, fileBasename);
if (!fs.existsSync(filePath)) {
return Promise.resolve(undefined);
const denoifyOut = parseAsDenoifyConfig({
"configFileType": await getFileTypeAndContent({
"getConfigFileRawContent": fileBasename => {
const filePath = pathJoin(moduleDirPath, fileBasename);
if (!fs.existsSync(filePath)) {
return Promise.resolve(undefined);
}
return Promise.resolve(fs.readFileSync(filePath).toString("utf8"));
}
return Promise.resolve(fs.readFileSync(filePath).toString("utf8"));
})
)?.out;
})?.out;

if (denoifyOut === undefined) {
return undefined;
Expand Down
82 changes: 82 additions & 0 deletions src/lib/config/fileAndContent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as YAML from "yaml";
import config from ".";

export type ConfigFileType =
| {
type: "absent";
}
| {
// yaml is superset of json, we can treat it as yaml instead
type: "js" | "yaml";
configFileBasename: string;
configFileRawContent: string;
};

function tryParseAsYamlCompatible(content: string) {
try {
return YAML.parse(content);
} catch {
return undefined;
}
}

function parseConfig({
configFileBasename,
configFileRawContent
}: {
configFileBasename: string | undefined;
configFileRawContent: string | undefined;
}): ConfigFileType {
if (
configFileBasename === undefined ||
configFileRawContent === undefined ||
!config.supportedConfigFile.find(configFile => configFileBasename.endsWith(configFile))
) {
return {
type: "absent"
};
}
if (
(configFileBasename === ".denoifyrc" ||
configFileBasename.endsWith(".json") ||
configFileBasename.endsWith(".yaml") ||
configFileBasename.endsWith(".yml")) &&
tryParseAsYamlCompatible(configFileRawContent)
) {
return {
type: "yaml",
configFileBasename,
configFileRawContent
};
}
if (configFileBasename.endsWith(".cjs") || configFileBasename.endsWith(".js")) {
return {
type: "js",
configFileBasename,
configFileRawContent
};
}
return {
type: "absent"
};
}

export default function getFileTypeAndContent({
getConfigFileRawContent
}: {
getConfigFileRawContent: (configFileBasename: string) => Promise<string | undefined>;
}) {
return config.supportedConfigFile.reduce(async (configFileType, configFileBasename) => {
if ((await configFileType).type !== "absent") {
return configFileType;
}
const configFileRawContent = await getConfigFileRawContent(configFileBasename);
if (!configFileRawContent || (configFileBasename === config.packageJson && !tryParseAsYamlCompatible(configFileRawContent)?.denoify)) {
return configFileType;
}
return parseConfig({
configFileBasename,
configFileRawContent
});
}, Promise.resolve({ type: "absent" }) as Promise<ConfigFileType>);
}
29 changes: 29 additions & 0 deletions src/lib/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { cosmiconfig } from "cosmiconfig";
import { parseAsDenoifyParams } from "./parseParams";

const config = (() => {
const packageJson = "package.json";
return {
packageJson,
"supportedConfigFile": (() => {
const denoify = "denoify";
const rcs = ["", ".json", ".yaml", ".yml"];
const configs = [".js", ".cjs"];
// in order of precedence
return [packageJson].concat(
rcs.concat(configs).map(extension => `.${denoify}rc${extension}`),
configs.map(extension => `${denoify}.config${extension}`)
);
})(),
"getDenoifyParamsWithCosmiconfig": async () => {
const explorer = cosmiconfig("denoify");
const search = await explorer.search();
if (search) {
console.log(`Configurations from ${search.filepath} are used`);
}
return parseAsDenoifyParams(search?.config ?? undefined);
}
} as const;
})();

export default config;
111 changes: 111 additions & 0 deletions src/lib/config/parseParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import * as fs from "fs";
import * as YAML from "yaml";
import { cosmiconfig } from "cosmiconfig";
import config from ".";
import { ConfigFileType } from "./fileAndContent";

export type DenoifyParams = {
replacer?: string;
ports?: {
[portName: string]: string;
};
out?: string;
index?: string;
includes?: (
| string
| {
src: string;
destDir?: string;
destBasename?: string;
}
)[];
};

function parseAsStringElseUndefined(param: unknown) {
return typeof param === "string" ? param : undefined;
}

function parseAsStringElseThrow({ param, type }: { param: unknown; type: string }) {
if (typeof param === "string") {
return param;
}
throw new Error(
[
"Denoify configuration Error",
`Expect ${type} to be string, got ${param} instead`,
"See: https://github.com/garronej/my_dummy_npm_and_deno_module"
].join("\n")
);
}

export function parseAsDenoifyParams(denoifyParams: any): DenoifyParams | undefined {
if (denoifyParams === undefined) {
return undefined;
}
const { includes } = denoifyParams;
return {
"replacer": parseAsStringElseUndefined(denoifyParams.replacer),
"out": parseAsStringElseUndefined(denoifyParams.out),
"index": parseAsStringElseUndefined(denoifyParams.index),
"includes": !Array.isArray(includes)
? undefined
: includes.map(elem =>
typeof elem === "string"
? elem
: {
"destDir": parseAsStringElseUndefined(elem.destDir),
"destBasename": parseAsStringElseUndefined(elem.destBasename),
"src": parseAsStringElseThrow({
"param": elem.src,
"type": "src in includes array"
})
}
),
"ports":
denoifyParams.ports !== undefined || denoifyParams.ports !== null
? undefined
: Object.entries(denoifyParams.ports).reduce(
(prev, [portName, value]) => ({
...prev,
[portName]: parseAsStringElseThrow({
"param": value,
"type": "value of ports object"
})
}),
{}
)
};
}

export function parseAsDenoifyConfig({ configFileType }: { configFileType: ConfigFileType }) {
switch (configFileType.type) {
case "absent":
return undefined;
case "yaml": {
const parsed = YAML.parse(configFileType.configFileRawContent);
return parseAsDenoifyParams(configFileType.configFileBasename !== config.packageJson ? parsed : parsed.denoify);
}
case "js": {
const denoifyCacheDirPath = "node_modules/.cache/denoify/cacheDirPath";
if (!fs.existsSync(denoifyCacheDirPath)) {
fs.mkdirSync(denoifyCacheDirPath, {
"recursive": true
});
}
const path = `${process.cwd()}/${denoifyCacheDirPath}/config.js`;
fs.writeFileSync(path, configFileType.configFileRawContent);
// cosmiconfig internally uses import-fresh to parse JS config
// import-fresh only support commonjs export, so we can use require
return parseAsDenoifyParams(require(path));
}
}
}

export async function getDenoifyParamsWithCosmiconfig() {
const explorer = cosmiconfig("denoify");
const search = await explorer.search();
if (search) {
console.log(`Configurations from ${search.filepath} are used`);
}
return parseAsDenoifyParams(search?.config ?? undefined);
}
2 changes: 1 addition & 1 deletion src/lib/denoify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { id } from "tsafe";
import { resolvePathsWithWildcards } from "../tools/resolvePathsWithWildcards";
import { arrPartition } from "evt/tools/reducers/partition";
import { fsCopy } from "../tools/fsCopy";
import { getDenoifyParamsWithCosmiconfig } from "./parseParams";
import { getDenoifyParamsWithCosmiconfig } from "./config/parseParams";

export async function denoify(params: {
projectPath: string;
Expand Down
Loading

0 comments on commit 391c636

Please sign in to comment.