Skip to content

Commit

Permalink
Add dot paths support (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
qetza committed Apr 21, 2024
1 parent 21f43e2 commit d3afb3c
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,7 @@

## v1.6.0
- Add support for case-insensitive glob pattern matching ([#13](https://github.com/qetza/replacetokens/issues/13)).
- Add support for dot paths in glob pattern matching ([#12](https://github.com/qetza/replacetokens/issues/12)).
- Remove `readTextFile` function.

## v1.5.0
Expand Down
10 changes: 8 additions & 2 deletions README.md
Expand Up @@ -23,6 +23,7 @@ replacetokens --sources
[--escape {auto, off, json, xml, custom}]
[--escape-char]
[--help]
[--include-dot-paths]
[--log-level {debug, info, warn, error, off}]
[--missing-var-action {none, keep, replace}]
[--missing-var-default]
Expand Down Expand Up @@ -76,7 +77,7 @@ Add BOM when writing files.

`--case-insensitive-paths`

Enable case-insensitive file path matching in glob patterns (sources and variables).
Enable case-insensitive file path matching in glob patterns (_sources_ and _variables_).

`--chars-to-escape <string>`

Expand Down Expand Up @@ -109,6 +110,10 @@ The escape character to use when using `custom` escape.

Show help.

`--include-dot-paths`

Include directories and files starting with a dot (`.`) in glob matching results (_sources_ and _variables_).

`--log-level <string>`

The log level. Default is `info`.
Expand Down Expand Up @@ -276,7 +281,7 @@ See CLI documentation for the parsing pattern and constraints.

options:
- `caseInsensitive` _(default: false)_: enable case-insensitive matching in file paths
- `dot` _(default: false)_: allow patterns to match entries starting with a dot (`.`)
- `dot` _(default: false)_: include directories and files starting with a dot (`.`) in glob matching results
- `normalizeWin32` _(default: false)_: replace back-slashes (`\`) with forward-slashes (`/`) in file paths
- `root`: _(default: current working directory)_: root path used when reading files with relative paths
- `separator` _(default: .)_: the separator used when flattening the keys
Expand Down Expand Up @@ -315,6 +320,7 @@ options:
- `root` _(default: current working directory)_: root path used when reading files with relative paths
- `sources`: specifies glob pattern options
- `caseInsensitive` _(default: false)_: enable case-insensitive matching in file paths
- `dot` _(default: false)_: include directories and files starting with a dot (`.`) in glob matching results
- `token`: specifies the token pattern
- `pattern` _(default: default)_: the token pattern
- `prefix` _(default: null)_: the token prefix if `pattern` is `custom`
Expand Down
11 changes: 9 additions & 2 deletions src/bin/run.ts
Expand Up @@ -45,6 +45,11 @@ export async function run() {
description: 'value escaping'
},
'escape-char': { type: 'string', description: 'custom escape character' },
'include-dot-paths': {
type: 'boolean',
description:
"Include directories and files starting with a dot ('.') in glob matching results (sources and variables)."
},
'log-level': {
choice: ['debug', 'info', 'warn', 'error', 'off'],
default: 'info',
Expand Down Expand Up @@ -196,7 +201,8 @@ export async function run() {
separator: argv.separator,
normalizeWin32: false,
root: argv.root,
caseInsensitive: argv['case-insensitive-paths']
caseInsensitive: argv['case-insensitive-paths'],
dot: argv['include-dot-paths']
});
const result = await rt.replaceTokens(argv.sources, (name: string) => variables[name], {
root: argv.root,
Expand Down Expand Up @@ -227,7 +233,8 @@ export async function run() {
suffix: argv['transforms-suffix']
},
sources: {
caseInsensitive: argv['case-insensitive-paths']
caseInsensitive: argv['case-insensitive-paths'],
dot: argv['include-dot-paths']
}
});

Expand Down
6 changes: 4 additions & 2 deletions src/index.ts
Expand Up @@ -57,7 +57,7 @@ export interface Options {
readonly addBOM?: boolean;
readonly escape?: { readonly type?: string; readonly chars?: string; readonly escapeChar?: string };
readonly transforms?: { readonly enabled?: boolean; readonly prefix?: string; readonly suffix?: string };
readonly sources?: { readonly caseInsensitive?: boolean };
readonly sources?: { readonly caseInsensitive?: boolean; readonly dot?: boolean };
}

export class Counter {
Expand Down Expand Up @@ -237,7 +237,8 @@ export async function replaceTokens(
},
recursive: options?.recursive ?? false,
sources: {
caseInsensitive: options?.sources?.caseInsensitive ?? false
caseInsensitive: options?.sources?.caseInsensitive ?? false,
dot: options?.sources?.dot ?? false
},
token: {
pattern: options?.token?.pattern ?? TokenPatterns.Default,
Expand Down Expand Up @@ -308,6 +309,7 @@ export async function replaceTokens(
var inputs = await fg.glob(pattern.inputPatterns, {
absolute: true,
caseSensitiveMatch: !options.sources!.caseInsensitive,
dot: options.sources!.dot,
cwd: options.root,
onlyFiles: true,
unique: true
Expand Down
3 changes: 3 additions & 0 deletions tests/data/loadvariables/.vars.json
@@ -0,0 +1,3 @@
{
"var4": "value4"
}
5 changes: 4 additions & 1 deletion tests/loadvariables.test.ts
Expand Up @@ -60,6 +60,8 @@ describe('loadVariables', () => {
expect(consoleSpies.debug).toHaveBeenCalledWith(`loading from file '${path.join(data, 'vars.json')}'`);
expect(consoleSpies.debug).toHaveBeenCalledWith(`loading from file '${path.join(data, 'vars.yml')}'`);
expect(consoleSpies.debug).toHaveBeenCalledWith(`loading from file '${path.join(data, 'vars.yaml')}'`);
expect(consoleSpies.debug).not.toHaveBeenCalledWith(`loading from file '${path.join(data, '.vars.json')}'`);
expect(consoleSpies.debug).not.toHaveBeenCalledWith(`loading from file '${path.join(data, '.vars', 'var.json')}'`);

expect(result).toEqual({
VAR1: 'value1',
Expand Down Expand Up @@ -230,9 +232,10 @@ describe('loadVariables', () => {
// assert
expect(consoleSpies.debug).toHaveBeenCalledWith(`loading from file '${path.join(data, 'var.jsonc')}'`);
expect(consoleSpies.debug).toHaveBeenCalledWith(`loading from file '${path.join(data, 'vars.json')}'`);
expect(consoleSpies.debug).toHaveBeenCalledWith(`loading from file '${path.join(data, '.vars.json')}'`);
expect(consoleSpies.debug).toHaveBeenCalledWith(`loading from file '${path.join(data, '.vars', 'var.json')}'`);

expect(result).toEqual({ VAR1: 'value1', VAR2: 'file', 'VAR2.SUB2.0': 'value2', VAR3: 'file' });
expect(result).toEqual({ VAR1: 'value1', VAR2: 'file', 'VAR2.SUB2.0': 'value2', VAR3: 'file', VAR4: 'value4' });
});

it('options: caseInsensitive', async () => {
Expand Down
23 changes: 23 additions & 0 deletions tests/replacetokens.test.ts
Expand Up @@ -21,6 +21,8 @@ describe('replaceTokens', () => {

async function copyData(source: string, dest: string): Promise<string> {
dest = path.join(tmp, dest);

await fs.mkdir(path.dirname(dest), { recursive: true });
await fs.copyFile(path.join(data, source), dest);

return path.resolve(dest);
Expand Down Expand Up @@ -276,6 +278,27 @@ describe('replaceTokens', () => {
expectCountersToEqual(result, 0, 1, 2, 2, 0);
await expectFileToEqual(input, 'default.expected.json');
});

it('dot paths', async () => {
// arrange
const input1 = await copyData('default.json', '.default1.json');
const input2 = await copyData('default.json', '.data/default1.json');
spyOnConsole();

// act
const result = await replaceTokens(
normalizeSources(path.join(tmp, '**/*.json')),
getVariableCallback({ var1: 'var1_value', var2: 'var2_value' }),
{
sources: { dot: true }
}
);

// assert
expectCountersToEqual(result, 0, 2, 4, 4, 0);
await expectFileToEqual(input1, 'default.expected.json');
await expectFileToEqual(input2, 'default.expected.json');
});
});

describe('variables', () => {
Expand Down
20 changes: 19 additions & 1 deletion tests/run.test.ts
Expand Up @@ -287,7 +287,8 @@ describe('run', () => {
suffix: ')'
},
sources: {
caseInsensitive: undefined
caseInsensitive: undefined,
dot: undefined
}
});

Expand Down Expand Up @@ -762,6 +763,23 @@ describe('run', () => {
);
});

it('include-dot-paths', async () => {
// arrange
jest.replaceProperty(process, 'argv', argv('--include-dot-paths'));

// act
await run();

// assert
expect(loadVariablesSpy).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ dot: true }));

expect(replaceTokensSpy).toHaveBeenCalledWith(
expect.anything(),
expect.any(Function),
expect.objectContaining({ sources: expect.objectContaining({ dot: true }) })
);
});

it('replace tokens', async () => {
// arrange
await fs.mkdir(tmp, { recursive: true });
Expand Down

0 comments on commit d3afb3c

Please sign in to comment.