fix: add cli
This commit is contained in:
parent
ca5f4984a4
commit
733b697ee2
15
package.json
15
package.json
@ -32,7 +32,8 @@
|
|||||||
"publish": "npm run build && npm publish ./dist",
|
"publish": "npm run build && npm publish ./dist",
|
||||||
"coverage": "vitest run --coverage",
|
"coverage": "vitest run --coverage",
|
||||||
"lint": "ultracite lint",
|
"lint": "ultracite lint",
|
||||||
"format": "ultracite format"
|
"format": "ultracite format",
|
||||||
|
"cli": "tsx scripts/cli.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ngify/http": "^2.0.4",
|
"@ngify/http": "^2.0.4",
|
||||||
@ -43,18 +44,24 @@
|
|||||||
"rxjs": "^7.4.0||>=8.0.0"
|
"rxjs": "^7.4.0||>=8.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "1.9.4",
|
||||||
|
"@biomejs/js-api": "0.7.1",
|
||||||
|
"@biomejs/wasm-nodejs": "^1.9.4",
|
||||||
"@evilmartians/lefthook": "^1.0.3",
|
"@evilmartians/lefthook": "^1.0.3",
|
||||||
"@playwright/test": "^1.49.1",
|
"@playwright/test": "^1.49.1",
|
||||||
"@rslib/core": "^0.3.1",
|
"@rslib/core": "^0.3.1",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/node": "^22.10.1",
|
|
||||||
"@vitest/coverage-v8": "^3.0.1",
|
"@vitest/coverage-v8": "^3.0.1",
|
||||||
|
"commander": "^13.1.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
"oxc-parser": "^0.48.1",
|
||||||
|
"oxc-walker": "^0.2.2",
|
||||||
"rfc4648": "^1.5.0",
|
"rfc4648": "^1.5.0",
|
||||||
|
"rxjs": "^7.4.0",
|
||||||
|
"tsx": "^4.19.2",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"ultracite": "^4.1.15",
|
"ultracite": "^4.1.15",
|
||||||
"vitest": "^3.0.1",
|
"vitest": "^3.0.1"
|
||||||
"rxjs": "^7.4.0"
|
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"rxjs",
|
"rxjs",
|
||||||
|
614
pnpm-lock.yaml
generated
614
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
19
scripts/cli.ts
Normal file
19
scripts/cli.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import { Command } from 'commander';
|
||||||
|
import { rewriteAllObservableSubscribeToLastValueFrom } from './code-transform';
|
||||||
|
|
||||||
|
const program = new Command();
|
||||||
|
|
||||||
|
program
|
||||||
|
.version('1.0.0')
|
||||||
|
.description('A CLI tool to help develop oidc-client-rx');
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('rewrite <pattern>')
|
||||||
|
.description('Rewrite files matching the given glob pattern')
|
||||||
|
.action(async (pattern: string) => {
|
||||||
|
await rewriteAllObservableSubscribeToLastValueFrom(pattern);
|
||||||
|
});
|
||||||
|
|
||||||
|
program.parse(process.argv);
|
35
scripts/code-transform.spec.ts
Normal file
35
scripts/code-transform.spec.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { describe, it } from 'node:test';
|
||||||
|
import { Biome, Distribution } from '@biomejs/js-api';
|
||||||
|
import { rewriteObservableSubscribeToLastValueFrom } from './code-transform';
|
||||||
|
|
||||||
|
describe('writeAllSpecObservableSubscribeToLastValueFrom', () => {
|
||||||
|
it('should transform valid string', async () => {
|
||||||
|
const actual = await rewriteObservableSubscribeToLastValueFrom(
|
||||||
|
'index.ts',
|
||||||
|
`refreshSessionIframeService
|
||||||
|
.refreshSessionWithIframe(allConfigs[0]!, allConfigs)
|
||||||
|
.subscribe((result) => {
|
||||||
|
expect(
|
||||||
|
result
|
||||||
|
).toHaveBeenCalledExactlyOnceWith(
|
||||||
|
'a-url',
|
||||||
|
allConfigs[0]!,
|
||||||
|
allConfigs
|
||||||
|
);
|
||||||
|
});`
|
||||||
|
);
|
||||||
|
|
||||||
|
const expect = `const result = await lastValueFrom(refreshSessionIframeService.refreshSessionWithIframe(allConfigs[0]!, allConfigs));
|
||||||
|
expect(result).toHaveBeenCalledExactlyOnceWith('a-url',allConfigs[0]!,allConfigs);`;
|
||||||
|
|
||||||
|
const biome = await Biome.create({
|
||||||
|
distribution: Distribution.NODE,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
biome.formatContent(actual, { filePath: 'index.ts' }).content,
|
||||||
|
biome.formatContent(expect, { filePath: 'index.ts' }).content
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
113
scripts/code-transform.ts
Normal file
113
scripts/code-transform.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import fsp from 'node:fs/promises';
|
||||||
|
import {
|
||||||
|
type ClassElement,
|
||||||
|
type MagicString,
|
||||||
|
type Statement,
|
||||||
|
parseSync,
|
||||||
|
} from 'oxc-parser';
|
||||||
|
import { type Node, walk } from 'oxc-walker';
|
||||||
|
|
||||||
|
function sourceTextFromNode(
|
||||||
|
context: { magicString?: MagicString },
|
||||||
|
node: Node
|
||||||
|
): string {
|
||||||
|
const magicString = context.magicString;
|
||||||
|
assert(magicString, 'magicString should be defined');
|
||||||
|
const start = node.start;
|
||||||
|
const end = node.end;
|
||||||
|
return magicString.getSourceText(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function rewriteObservableSubscribeToLastValueFrom(
|
||||||
|
filename: string,
|
||||||
|
content?: string
|
||||||
|
) {
|
||||||
|
const code = content ?? (await fsp.readFile(filename, 'utf-8'));
|
||||||
|
const parsedResult = parseSync('index.ts', code);
|
||||||
|
const magicString = parsedResult.magicString;
|
||||||
|
walk(parsedResult, {
|
||||||
|
leave(node, _, context) {
|
||||||
|
const transformExprs = <T extends Statement[]>(
|
||||||
|
children: T
|
||||||
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: <explanation>
|
||||||
|
): T => {
|
||||||
|
const newChildren: T = [] as any as T;
|
||||||
|
for (const child of children) {
|
||||||
|
if (
|
||||||
|
child.type === 'ExpressionStatement' &&
|
||||||
|
child.expression.type === 'CallExpression' &&
|
||||||
|
child.expression.callee.type === 'StaticMemberExpression' &&
|
||||||
|
child.expression.callee.property.name === 'subscribe' &&
|
||||||
|
child.expression.arguments[0]?.type === 'ArrowFunctionExpression' &&
|
||||||
|
child.expression.arguments[0].body.type === 'FunctionBody'
|
||||||
|
) {
|
||||||
|
const awaited =
|
||||||
|
child.expression.arguments[0].params.kind ===
|
||||||
|
'ArrowFormalParameters' &&
|
||||||
|
child.expression.arguments[0].params.items[0]?.type ===
|
||||||
|
'FormalParameter' &&
|
||||||
|
child.expression.arguments[0].params.items[0].pattern.type ===
|
||||||
|
'Identifier'
|
||||||
|
? child.expression.arguments[0].params.items[0].pattern.name
|
||||||
|
: undefined;
|
||||||
|
const newContent =
|
||||||
|
(awaited
|
||||||
|
? `const ${awaited} = await lastValueFrom(${sourceTextFromNode(
|
||||||
|
context,
|
||||||
|
child.expression.callee.object
|
||||||
|
)});\n`
|
||||||
|
: `await lastValueFrom(${sourceTextFromNode(context, child.expression.callee.object)});\n`) +
|
||||||
|
child.expression.arguments[0].body.statements
|
||||||
|
.map((s) => sourceTextFromNode(context, s))
|
||||||
|
.join(';\n');
|
||||||
|
|
||||||
|
const newStatements = parseSync('index.ts', newContent).program
|
||||||
|
.body as any[];
|
||||||
|
|
||||||
|
magicString.remove(child.start, child.end);
|
||||||
|
magicString.appendRight(child.start, newContent);
|
||||||
|
|
||||||
|
newChildren.push(...newStatements);
|
||||||
|
} else {
|
||||||
|
newChildren.push(child as any);
|
||||||
|
}
|
||||||
|
return newChildren;
|
||||||
|
}
|
||||||
|
return newChildren;
|
||||||
|
};
|
||||||
|
if ('body' in node && Array.isArray(node.body) && node.body.length > 0) {
|
||||||
|
const children = node.body;
|
||||||
|
node.body = transformExprs(children as any)!;
|
||||||
|
} else if (
|
||||||
|
'body' in node &&
|
||||||
|
node.body &&
|
||||||
|
'type' in node.body &&
|
||||||
|
node.body.type === 'FunctionBody'
|
||||||
|
) {
|
||||||
|
console.error('xxx', node.body.type);
|
||||||
|
const children = node.body.statements;
|
||||||
|
node.body.statements = transformExprs(children)!;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = magicString.toString();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function rewriteAllObservableSubscribeToLastValueFrom(
|
||||||
|
pattern: string | string[]
|
||||||
|
) {
|
||||||
|
const files = fsp.glob(pattern);
|
||||||
|
for await (const file of files) {
|
||||||
|
const source = await fsp.readFile(file, 'utf-8');
|
||||||
|
const result = await rewriteObservableSubscribeToLastValueFrom(file);
|
||||||
|
if (source !== result) {
|
||||||
|
console.error('not equal');
|
||||||
|
}
|
||||||
|
|
||||||
|
await fsp.writeFile(file, result, 'utf-8');
|
||||||
|
}
|
||||||
|
}
|
@ -346,11 +346,9 @@ describe('CheckSessionService', () => {
|
|||||||
|
|
||||||
describe('checkSessionChanged$', () => {
|
describe('checkSessionChanged$', () => {
|
||||||
it('emits when internal event is thrown', async () => {
|
it('emits when internal event is thrown', async () => {
|
||||||
checkSessionService.checkSessionChanged$
|
const result = await lastValueFrom(checkSessionService.checkSessionChanged$
|
||||||
.pipe(skip(1))
|
.pipe(skip(1)));
|
||||||
.subscribe((result) => {
|
expect(result).toBe(true);
|
||||||
expect(result).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
const serviceAsAny = checkSessionService as any;
|
const serviceAsAny = checkSessionService as any;
|
||||||
|
|
||||||
@ -358,9 +356,8 @@ describe('CheckSessionService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('emits false initially', async () => {
|
it('emits false initially', async () => {
|
||||||
checkSessionService.checkSessionChanged$.subscribe((result) => {
|
const result = await lastValueFrom(checkSessionService.checkSessionChanged$);
|
||||||
expect(result).toBe(false);
|
expect(result).toBe(false);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('emits false then true when emitted', async () => {
|
it('emits false then true when emitted', async () => {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { DOCUMENT } from '../dom';
|
import { Injectable, NgZone, type OnDestroy, inject } from 'injection-js';
|
||||||
import { Injectable, NgZone, OnDestroy, inject } from 'injection-js';
|
|
||||||
import { BehaviorSubject, Observable, of } from 'rxjs';
|
import { BehaviorSubject, Observable, of } from 'rxjs';
|
||||||
import { take } from 'rxjs/operators';
|
import { take } from 'rxjs/operators';
|
||||||
import { OpenIdConfiguration } from '../config/openid-configuration';
|
import type { OpenIdConfiguration } from '../config/openid-configuration';
|
||||||
|
import { DOCUMENT } from '../dom';
|
||||||
import { LoggerService } from '../logging/logger.service';
|
import { LoggerService } from '../logging/logger.service';
|
||||||
import { EventTypes } from '../public-events/event-types';
|
import { EventTypes } from '../public-events/event-types';
|
||||||
import { PublicEventsService } from '../public-events/public-events.service';
|
import { PublicEventsService } from '../public-events/public-events.service';
|
||||||
@ -74,7 +74,7 @@ export class CheckSessionService implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
start(configuration: OpenIdConfiguration): void {
|
start(configuration: OpenIdConfiguration): void {
|
||||||
if (!!this.scheduledHeartBeatRunning) {
|
if (this.scheduledHeartBeatRunning) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,13 +141,13 @@ export class CheckSessionService implements OnDestroy {
|
|||||||
return of();
|
return of();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!contentWindow) {
|
if (contentWindow) {
|
||||||
|
contentWindow.location.replace(checkSessionIframe);
|
||||||
|
} else {
|
||||||
this.loggerService.logWarning(
|
this.loggerService.logWarning(
|
||||||
configuration,
|
configuration,
|
||||||
'CheckSession - init check session: IFrame contentWindow does not exist'
|
'CheckSession - init check session: IFrame contentWindow does not exist'
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
contentWindow.location.replace(checkSessionIframe);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Observable((observer) => {
|
return new Observable((observer) => {
|
||||||
@ -197,7 +197,7 @@ export class CheckSessionService implements OnDestroy {
|
|||||||
|
|
||||||
this.outstandingMessages++;
|
this.outstandingMessages++;
|
||||||
contentWindow.postMessage(
|
contentWindow.postMessage(
|
||||||
clientId + ' ' + sessionState,
|
`${clientId} ${sessionState}`,
|
||||||
iframeOrigin
|
iframeOrigin
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { TestBed } from '@/testing';
|
import { TestBed } from '@/testing';
|
||||||
import { of } from 'rxjs';
|
import { lastValueFrom, of } from 'rxjs';
|
||||||
import { vi } from 'vitest';
|
import { vi } from 'vitest';
|
||||||
import { LoggerService } from '../logging/logger.service';
|
import { LoggerService } from '../logging/logger.service';
|
||||||
import { mockProvider } from '../testing/mock';
|
import { mockProvider } from '../testing/mock';
|
||||||
@ -20,9 +20,6 @@ describe('RefreshSessionIframeService ', () => {
|
|||||||
mockProvider(UrlService),
|
mockProvider(UrlService),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
refreshSessionIframeService = TestBed.inject(RefreshSessionIframeService);
|
refreshSessionIframeService = TestBed.inject(RefreshSessionIframeService);
|
||||||
urlService = TestBed.inject(UrlService);
|
urlService = TestBed.inject(UrlService);
|
||||||
});
|
});
|
||||||
@ -62,7 +59,9 @@ describe('RefreshSessionIframeService ', () => {
|
|||||||
it('dispatches customevent to window object', async () => {
|
it('dispatches customevent to window object', async () => {
|
||||||
const dispatchEventSpy = vi.spyOn(window, 'dispatchEvent');
|
const dispatchEventSpy = vi.spyOn(window, 'dispatchEvent');
|
||||||
|
|
||||||
(refreshSessionIframeService as any).initSilentRenewRequest();
|
await lastValueFrom(
|
||||||
|
(refreshSessionIframeService as any).initSilentRenewRequest()
|
||||||
|
);
|
||||||
|
|
||||||
expect(dispatchEventSpy).toHaveBeenCalledExactlyOnceWith(
|
expect(dispatchEventSpy).toHaveBeenCalledExactlyOnceWith(
|
||||||
new CustomEvent('oidc-silent-renew-init', {
|
new CustomEvent('oidc-silent-renew-init', {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { TestBed } from '@/testing';
|
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
|
||||||
import { of } from 'rxjs';
|
import { lastValueFrom, of } from 'rxjs';
|
||||||
import { vi } from 'vitest';
|
import { vi } from 'vitest';
|
||||||
import type { OpenIdConfiguration } from '../../config/openid-configuration';
|
import type { OpenIdConfiguration } from '../../config/openid-configuration';
|
||||||
import { FlowsDataService } from '../../flows/flows-data.service';
|
import { FlowsDataService } from '../../flows/flows-data.service';
|
||||||
@ -1044,10 +1044,9 @@ describe('UrlService Tests', () => {
|
|||||||
|
|
||||||
describe('getAuthorizeUrl', () => {
|
describe('getAuthorizeUrl', () => {
|
||||||
it('returns null if no config is given', async () => {
|
it('returns null if no config is given', async () => {
|
||||||
service.getAuthorizeUrl(null).subscribe((url) => {
|
const url = await lastValueFrom(service.getAuthorizeUrl(null));
|
||||||
expect(url).toBeNull();
|
expect(url).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('returns null if current flow is code flow and no redirect url is defined', async () => {
|
it('returns null if current flow is code flow and no redirect url is defined', async () => {
|
||||||
vi.spyOn(flowHelper, 'isCurrentFlowCodeFlow').mockReturnValue(true);
|
vi.spyOn(flowHelper, 'isCurrentFlowCodeFlow').mockReturnValue(true);
|
||||||
@ -1383,7 +1382,7 @@ describe('UrlService Tests', () => {
|
|||||||
|
|
||||||
resultObs$.subscribe((result) => {
|
resultObs$.subscribe((result) => {
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
`client_id=testClientId&redirect_uri=testRedirectUrl&response_type=testResponseType&scope=testScope&nonce=testNonce&state=testState&code_challenge=testCodeChallenge&code_challenge_method=S256`
|
'client_id=testClientId&redirect_uri=testRedirectUrl&response_type=testResponseType&scope=testScope&nonce=testNonce&state=testState&code_challenge=testCodeChallenge&code_challenge_method=S256'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -1414,7 +1413,7 @@ describe('UrlService Tests', () => {
|
|||||||
|
|
||||||
resultObs$.subscribe((result) => {
|
resultObs$.subscribe((result) => {
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
`client_id=testClientId&redirect_uri=testRedirectUrl&response_type=testResponseType&scope=testScope&nonce=testNonce&state=testState&code_challenge=testCodeChallenge&code_challenge_method=S256&hd=testHdParam`
|
'client_id=testClientId&redirect_uri=testRedirectUrl&response_type=testResponseType&scope=testScope&nonce=testNonce&state=testState&code_challenge=testCodeChallenge&code_challenge_method=S256&hd=testHdParam'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -1445,7 +1444,7 @@ describe('UrlService Tests', () => {
|
|||||||
|
|
||||||
resultObs$.subscribe((result) => {
|
resultObs$.subscribe((result) => {
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
`client_id=testClientId&redirect_uri=testRedirectUrl&response_type=testResponseType&scope=testScope&nonce=testNonce&state=testState&code_challenge=testCodeChallenge&code_challenge_method=S256&hd=testHdParam&any=thing`
|
'client_id=testClientId&redirect_uri=testRedirectUrl&response_type=testResponseType&scope=testScope&nonce=testNonce&state=testState&code_challenge=testCodeChallenge&code_challenge_method=S256&hd=testHdParam&any=thing'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -1480,7 +1479,7 @@ describe('UrlService Tests', () => {
|
|||||||
|
|
||||||
resultObs$.subscribe((result) => {
|
resultObs$.subscribe((result) => {
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
`client_id=testClientId&redirect_uri=testRedirectUrl&response_type=testResponseType&scope=testScope&nonce=testNonce&state=testState&code_challenge=testCodeChallenge&code_challenge_method=S256&hd=testHdParam&any=thing&any=otherThing`
|
'client_id=testClientId&redirect_uri=testRedirectUrl&response_type=testResponseType&scope=testScope&nonce=testNonce&state=testState&code_challenge=testCodeChallenge&code_challenge_method=S256&hd=testHdParam&any=thing&any=otherThing'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -1904,8 +1903,7 @@ describe('UrlService Tests', () => {
|
|||||||
|
|
||||||
resultObs$.subscribe((result: any) => {
|
resultObs$.subscribe((result: any) => {
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
`authorizationEndpoint?client_id=clientId&redirect_uri=http%3A%2F%2Fany-url.com` +
|
`authorizationEndpoint?client_id=clientId&redirect_uri=http%3A%2F%2Fany-url.com&response_type=${responseType}&scope=${scope}&nonce=${nonce}&state=${state}&to=add&as=well`
|
||||||
`&response_type=${responseType}&scope=${scope}&nonce=${nonce}&state=${state}&to=add&as=well`
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -2121,7 +2119,8 @@ describe('UrlService Tests', () => {
|
|||||||
const value = service.getEndSessionUrl(config);
|
const value = service.getEndSessionUrl(config);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
const expectValue = `something.auth0.com/v2/logout?client_id=someClientId&returnTo=https://localhost:1234/unauthorized`;
|
const expectValue =
|
||||||
|
'something.auth0.com/v2/logout?client_id=someClientId&returnTo=https://localhost:1234/unauthorized';
|
||||||
|
|
||||||
expect(value).toEqual(expectValue);
|
expect(value).toEqual(expectValue);
|
||||||
});
|
});
|
||||||
|
@ -25,6 +25,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./tsconfig.spec.json"
|
"path": "./tsconfig.spec.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.scripts.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
12
tsconfig.scripts.json
Normal file
12
tsconfig.scripts.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": ".",
|
||||||
|
"composite": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"include": ["scripts/"]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user