mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: cleanup runtimeGuid and other builtins bits (#36035)
This commit is contained in:
parent
28e925c001
commit
fd7d249ed4
@ -18,13 +18,6 @@ import { serializeAsCallArgument } from '@isomorphic/utilityScriptSerializers';
|
||||
|
||||
import type { SerializedValue } from '@isomorphic/utilityScriptSerializers';
|
||||
|
||||
// This runtime guid is replaced by the actual guid at runtime in all generated sources.
|
||||
const kRuntimeGuid = '$runtime_guid$';
|
||||
|
||||
// The name of the global playwright binding, referenced in Node.js.
|
||||
const kPlaywrightBinding = `__playwright__binding__${kRuntimeGuid}`;
|
||||
const kPlaywrightBindingController = `__playwright__binding__controller__${kRuntimeGuid}`;
|
||||
|
||||
export type BindingPayload = {
|
||||
name: string;
|
||||
seq: number;
|
||||
@ -37,14 +30,16 @@ type BindingData = {
|
||||
handles: Map<number, any>;
|
||||
};
|
||||
|
||||
class BindingsController {
|
||||
export class BindingsController {
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
private _global: typeof globalThis;
|
||||
private _globalBindingName: string;
|
||||
private _bindings = new Map<string, BindingData>();
|
||||
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
constructor(global: typeof globalThis) {
|
||||
constructor(global: typeof globalThis, globalBindingName: string) {
|
||||
this._global = global;
|
||||
this._globalBindingName = globalBindingName;
|
||||
}
|
||||
|
||||
addBinding(bindingName: string, needsHandle: boolean) {
|
||||
@ -72,7 +67,7 @@ class BindingsController {
|
||||
}
|
||||
payload = { name: bindingName, seq, serializedArgs };
|
||||
}
|
||||
(this._global as any)[kPlaywrightBinding](JSON.stringify(payload));
|
||||
(this._global as any)[this._globalBindingName](JSON.stringify(payload));
|
||||
return promise;
|
||||
};
|
||||
}
|
||||
@ -93,10 +88,3 @@ class BindingsController {
|
||||
callbacks.delete(arg.seq);
|
||||
}
|
||||
}
|
||||
|
||||
export function ensureBindingsController() {
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
const global = globalThis;
|
||||
if (!(global as any)[kPlaywrightBindingController])
|
||||
(global as any)[kPlaywrightBindingController] = new BindingsController(global);
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ interface WebKitLegacyDeviceMotionEvent extends DeviceMotionEvent {
|
||||
}
|
||||
|
||||
export type InjectedScriptOptions = {
|
||||
isUnderTest?: boolean;
|
||||
isUnderTest: boolean;
|
||||
sdkLanguage: Language;
|
||||
// For strict error and codegen
|
||||
testIdAttributeName: string;
|
||||
@ -124,11 +124,10 @@ export class InjectedScript {
|
||||
constructor(window: Window & typeof globalThis, options: InjectedScriptOptions) {
|
||||
this.window = window;
|
||||
this.document = window.document;
|
||||
this.isUnderTest = options.isUnderTest;
|
||||
// Make sure builtins are created from "window". This is important for InjectedScript instantiated
|
||||
// inside a trace viewer snapshot, where "window" differs from "globalThis".
|
||||
const utilityScript = new UtilityScript(window);
|
||||
this.isUnderTest = options.isUnderTest ?? utilityScript.isUnderTest;
|
||||
this.utils.builtins = utilityScript.builtins;
|
||||
this.utils.builtins = new UtilityScript(window, options.isUnderTest).builtins;
|
||||
this._sdkLanguage = options.sdkLanguage;
|
||||
this._testIdAttributeNameForStrictErrorAndConsoleCodegen = options.testIdAttributeName;
|
||||
this._evaluator = new SelectorEvaluatorImpl();
|
||||
|
||||
@ -16,11 +16,6 @@
|
||||
|
||||
import { parseEvaluationResultValue, serializeAsCallArgument } from '@isomorphic/utilityScriptSerializers';
|
||||
|
||||
// --- This section should match javascript.ts and generated_injected_builtins.js ---
|
||||
|
||||
// This flag is replaced by true/false at runtime in all generated sources.
|
||||
const kUtilityScriptIsUnderTest = false;
|
||||
|
||||
// Keep in sync with eslint.config.mjs
|
||||
export type Builtins = {
|
||||
setTimeout: Window['setTimeout'],
|
||||
@ -38,8 +33,6 @@ export type Builtins = {
|
||||
Date: typeof window['Date'],
|
||||
};
|
||||
|
||||
// --- End of the matching section ---
|
||||
|
||||
export class UtilityScript {
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
readonly global: typeof globalThis;
|
||||
@ -48,9 +41,9 @@ export class UtilityScript {
|
||||
readonly isUnderTest: boolean;
|
||||
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
constructor(global: typeof globalThis) {
|
||||
constructor(global: typeof globalThis, isUnderTest: boolean) {
|
||||
this.global = global;
|
||||
this.isUnderTest = kUtilityScriptIsUnderTest;
|
||||
this.isUnderTest = isUnderTest;
|
||||
if ((global as any).__pwClock) {
|
||||
this.builtins = (global as any).__pwClock.builtins;
|
||||
} else {
|
||||
|
||||
@ -21,7 +21,7 @@ import * as network from '../network';
|
||||
import { BidiConnection } from './bidiConnection';
|
||||
import { bidiBytesValueToString } from './bidiNetworkManager';
|
||||
import { BidiPage, kPlaywrightBindingChannel } from './bidiPage';
|
||||
import { kPlaywrightBinding } from '../javascript';
|
||||
import { PageBinding } from '../page';
|
||||
import * as bidi from './third_party/bidiProtocol';
|
||||
|
||||
import type { RegisteredListener } from '../utils/eventsHelper';
|
||||
@ -391,7 +391,7 @@ export class BidiBrowserContext extends BrowserContext {
|
||||
ownership: bidi.Script.ResultOwnership.Root,
|
||||
}
|
||||
}];
|
||||
const functionDeclaration = `function addMainBinding(callback) { globalThis['${kPlaywrightBinding}'] = callback; }`;
|
||||
const functionDeclaration = `function addMainBinding(callback) { globalThis['${PageBinding.kBindingName}'] = callback; }`;
|
||||
const promises = [];
|
||||
promises.push(this._browser._browserSession.send('script.addPreloadScript', {
|
||||
functionDeclaration,
|
||||
|
||||
@ -34,9 +34,7 @@ import { Recorder } from './recorder';
|
||||
import { RecorderApp } from './recorder/recorderApp';
|
||||
import { Selectors } from './selectors';
|
||||
import { Tracing } from './trace/recorder/tracing';
|
||||
import * as js from './javascript';
|
||||
import * as rawStorageSource from '../generated/storageScriptSource';
|
||||
import * as rawBindingsControllerSource from '../generated/bindingsControllerSource';
|
||||
|
||||
import type { Artifact } from './artifact';
|
||||
import type { Browser, BrowserOptions } from './browser';
|
||||
@ -327,13 +325,7 @@ export abstract class BrowserContext extends SdkObject {
|
||||
this._playwrightBindingExposed = true;
|
||||
await this.doExposePlaywrightBinding();
|
||||
|
||||
this.bindingsInitScript = new InitScript(`
|
||||
(() => {
|
||||
const module = {};
|
||||
${js.prepareGeneratedScript(rawBindingsControllerSource.source)}
|
||||
(module.exports.ensureBindingsController())();
|
||||
})();
|
||||
`, true /* internal */);
|
||||
this.bindingsInitScript = PageBinding.createInitScript();
|
||||
this.initScripts.push(this.bindingsInitScript);
|
||||
await this.doAddInitScript(this.bindingsInitScript);
|
||||
await this.safeNonStallingEvaluateInAllFrames(this.bindingsInitScript.source, 'main');
|
||||
@ -530,7 +522,7 @@ export abstract class BrowserContext extends SdkObject {
|
||||
|
||||
const collectScript = `(() => {
|
||||
const module = {};
|
||||
${js.prepareGeneratedScript(rawStorageSource.source)}
|
||||
${rawStorageSource.source}
|
||||
const script = new (module.exports.StorageScript())(${this._browser.options.name === 'firefox'});
|
||||
return script.collect(${indexedDB});
|
||||
})()`;
|
||||
@ -628,7 +620,7 @@ export abstract class BrowserContext extends SdkObject {
|
||||
await frame.goto(metadata, originState.origin, { timeout: 0 });
|
||||
const restoreScript = `(() => {
|
||||
const module = {};
|
||||
${js.prepareGeneratedScript(rawStorageSource.source)}
|
||||
${rawStorageSource.source}
|
||||
const script = new (module.exports.StorageScript())(${this._browser.options.name === 'firefox'});
|
||||
return script.restore(${JSON.stringify(originState)});
|
||||
})()`;
|
||||
|
||||
@ -26,8 +26,7 @@ import * as dom from '../dom';
|
||||
import * as frames from '../frames';
|
||||
import { helper } from '../helper';
|
||||
import * as network from '../network';
|
||||
import { kPlaywrightBinding } from '../javascript';
|
||||
import { Page, Worker } from '../page';
|
||||
import { Page, PageBinding, Worker } from '../page';
|
||||
import { registry } from '../registry';
|
||||
import { getAccessibilityTree } from './crAccessibility';
|
||||
import { CRBrowserContext } from './crBrowser';
|
||||
@ -1071,7 +1070,7 @@ class FrameSession {
|
||||
}
|
||||
|
||||
async exposePlaywrightBinding() {
|
||||
await this._client.send('Runtime.addBinding', { name: kPlaywrightBinding });
|
||||
await this._client.send('Runtime.addBinding', { name: PageBinding.kBindingName });
|
||||
}
|
||||
|
||||
async _getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
import * as rawClockSource from '../generated/clockSource';
|
||||
import { prepareGeneratedScript } from './javascript';
|
||||
|
||||
import type { BrowserContext } from './browserContext';
|
||||
|
||||
@ -85,7 +84,7 @@ export class Clock {
|
||||
this._scriptInstalled = true;
|
||||
const script = `(() => {
|
||||
const module = {};
|
||||
${prepareGeneratedScript(rawClockSource.source)}
|
||||
${rawClockSource.source}
|
||||
globalThis.__pwClock = (module.exports.inject())(globalThis);
|
||||
})();`;
|
||||
await this._browserContext.addInitScript(script);
|
||||
|
||||
@ -18,7 +18,6 @@ import { Page } from '../page';
|
||||
import { Dispatcher } from './dispatcher';
|
||||
import { PageDispatcher } from './pageDispatcher';
|
||||
import * as rawWebSocketMockSource from '../../generated/webSocketMockSource';
|
||||
import { prepareGeneratedScript } from '../javascript';
|
||||
import { createGuid } from '../utils/crypto';
|
||||
import { urlMatches } from '../../utils/isomorphic/urlMatch';
|
||||
import { eventsHelper } from '../utils/eventsHelper';
|
||||
@ -97,7 +96,7 @@ export class WebSocketRouteDispatcher extends Dispatcher<{ guid: string }, chann
|
||||
await target.addInitScript(`
|
||||
(() => {
|
||||
const module = {};
|
||||
${prepareGeneratedScript(rawWebSocketMockSource.source)}
|
||||
${rawWebSocketMockSource.source}
|
||||
(module.exports.inject())(globalThis);
|
||||
})();
|
||||
`, kInitScriptName);
|
||||
|
||||
@ -18,7 +18,7 @@ import fs from 'fs';
|
||||
|
||||
import * as js from './javascript';
|
||||
import { ProgressController } from './progress';
|
||||
import { asLocator } from '../utils';
|
||||
import { asLocator, isUnderTest } from '../utils';
|
||||
import { prepareFilesForUpload } from './fileUploadUtils';
|
||||
import { isSessionClosedError } from './protocolError';
|
||||
import * as rawInjectedScriptSource from '../generated/injectedScriptSource';
|
||||
@ -89,6 +89,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
||||
customEngines.push({ name, source });
|
||||
const sdkLanguage = this.frame.attribution.playwright.options.sdkLanguage;
|
||||
const options: InjectedScriptOptions = {
|
||||
isUnderTest: isUnderTest(),
|
||||
sdkLanguage,
|
||||
testIdAttributeName: selectorsRegistry.testIdAttributeName(),
|
||||
stableRafCount: this.frame._page.delegate.rafCountForStablePosition(),
|
||||
@ -99,7 +100,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
||||
const source = `
|
||||
(() => {
|
||||
const module = {};
|
||||
${js.prepareGeneratedScript(rawInjectedScriptSource.source)}
|
||||
${rawInjectedScriptSource.source}
|
||||
return new (module.exports.InjectedScript())(globalThis, ${JSON.stringify(options)});
|
||||
})();
|
||||
`;
|
||||
|
||||
@ -19,10 +19,10 @@ import { assert } from '../../utils';
|
||||
import { Browser } from '../browser';
|
||||
import { BrowserContext, verifyGeolocation } from '../browserContext';
|
||||
import { TargetClosedError } from '../errors';
|
||||
import { kPlaywrightBinding } from '../javascript';
|
||||
import * as network from '../network';
|
||||
import { ConnectionEvents, FFConnection } from './ffConnection';
|
||||
import { FFPage } from './ffPage';
|
||||
import { PageBinding } from '../page';
|
||||
|
||||
import type { BrowserOptions } from '../browser';
|
||||
import type { SdkObject } from '../instrumentation';
|
||||
@ -393,7 +393,7 @@ export class FFBrowserContext extends BrowserContext {
|
||||
}
|
||||
|
||||
override async doExposePlaywrightBinding() {
|
||||
this._browser.session.send('Browser.addBinding', { browserContextId: this._browserContextId, name: kPlaywrightBinding, script: '' });
|
||||
this._browser.session.send('Browser.addBinding', { browserContextId: this._browserContextId, name: PageBinding.kBindingName, script: '' });
|
||||
}
|
||||
|
||||
onClosePersistent() {}
|
||||
|
||||
@ -16,40 +16,13 @@
|
||||
|
||||
import { SdkObject } from './instrumentation';
|
||||
import * as rawUtilityScriptSource from '../generated/utilityScriptSource';
|
||||
import { createGuid, isUnderTest } from '../utils';
|
||||
import { isUnderTest } from '../utils';
|
||||
import { serializeAsCallArgument } from '../utils/isomorphic/utilityScriptSerializers';
|
||||
import { LongStandingScope } from '../utils/isomorphic/manualPromise';
|
||||
|
||||
import type * as dom from './dom';
|
||||
import type { UtilityScript } from '@injected/utilityScript';
|
||||
|
||||
// --- This section should match utilityScript.ts and generated_injected_builtins.js ---
|
||||
|
||||
// Use in the web-facing names to avoid leaking Playwright to the pages.
|
||||
export const runtimeGuid = createGuid();
|
||||
|
||||
// Preprocesses any generated script to include the runtime guid.
|
||||
export function prepareGeneratedScript(source: string) {
|
||||
return source.replaceAll('$runtime_guid$', runtimeGuid).replace('kUtilityScriptIsUnderTest = false', `kUtilityScriptIsUnderTest = ${isUnderTest()}`);
|
||||
}
|
||||
|
||||
export const kUtilityScriptSource = prepareGeneratedScript(rawUtilityScriptSource.source);
|
||||
|
||||
// Include this code in any evaluated source to get access to the UtilityScript instance.
|
||||
export function accessUtilityScript() {
|
||||
return `globalThis['__playwright_utility_script__${runtimeGuid}']`;
|
||||
}
|
||||
|
||||
// The name of the global playwright binding, accessed by UtilityScript.
|
||||
export const kPlaywrightBinding = '__playwright__binding__' + runtimeGuid;
|
||||
|
||||
// Include this code in any evaluated source to get access to the BindingsController instance.
|
||||
export function accessBindingsController() {
|
||||
return `globalThis['__playwright__binding__controller__${runtimeGuid}']`;
|
||||
}
|
||||
|
||||
// --- End of the matching section ---
|
||||
|
||||
interface TaggedAsJSHandle<T> {
|
||||
__jshandle: T;
|
||||
}
|
||||
@ -130,8 +103,8 @@ export class ExecutionContext extends SdkObject {
|
||||
const source = `
|
||||
(() => {
|
||||
const module = {};
|
||||
${kUtilityScriptSource}
|
||||
return new (module.exports.UtilityScript())(globalThis);
|
||||
${rawUtilityScriptSource.source}
|
||||
return new (module.exports.UtilityScript())(globalThis, ${isUnderTest()});
|
||||
})();`;
|
||||
this._utilityScriptPromise = this._raceAgainstContextDestroyed(this.delegate.rawEvaluateHandle(this, source))
|
||||
.then(handle => {
|
||||
|
||||
@ -35,6 +35,7 @@ import { isInvalidSelectorError } from '../utils/isomorphic/selectorParser';
|
||||
import { ManualPromise } from '../utils/isomorphic/manualPromise';
|
||||
import { parseEvaluationResultValue } from '../utils/isomorphic/utilityScriptSerializers';
|
||||
import { compressCallLog } from './callLog';
|
||||
import * as rawBindingsControllerSource from '../generated/bindingsControllerSource';
|
||||
|
||||
import type { Artifact } from './artifact';
|
||||
import type * as dom from './dom';
|
||||
@ -849,6 +850,20 @@ export class Worker extends SdkObject {
|
||||
}
|
||||
|
||||
export class PageBinding {
|
||||
private static kController = '__playwright__binding__controller__';
|
||||
static kBindingName = '__playwright__binding__';
|
||||
|
||||
static createInitScript() {
|
||||
return new InitScript(`
|
||||
(() => {
|
||||
const module = {};
|
||||
${rawBindingsControllerSource.source}
|
||||
const property = '${PageBinding.kController}';
|
||||
if (!globalThis[property])
|
||||
globalThis[property] = new (module.exports.BindingsController())(globalThis, '${PageBinding.kBindingName}');
|
||||
})();
|
||||
`, true /* internal */);
|
||||
}
|
||||
|
||||
readonly name: string;
|
||||
readonly playwrightFunction: frames.FunctionWithSource;
|
||||
@ -859,7 +874,7 @@ export class PageBinding {
|
||||
constructor(name: string, playwrightFunction: frames.FunctionWithSource, needsHandle: boolean) {
|
||||
this.name = name;
|
||||
this.playwrightFunction = playwrightFunction;
|
||||
this.initScript = new InitScript(`${js.accessBindingsController()}.addBinding(${JSON.stringify(name)}, ${needsHandle})`, true /* internal */);
|
||||
this.initScript = new InitScript(`globalThis['${PageBinding.kController}'].addBinding(${JSON.stringify(name)}, ${needsHandle})`, true /* internal */);
|
||||
this.needsHandle = needsHandle;
|
||||
this.internal = name.startsWith('__pw');
|
||||
}
|
||||
@ -873,7 +888,7 @@ export class PageBinding {
|
||||
throw new Error(`Function "${name}" is not exposed`);
|
||||
let result: any;
|
||||
if (binding.needsHandle) {
|
||||
const handle = await context.evaluateExpressionHandle(`arg => ${js.accessBindingsController()}.takeBindingHandle(arg)`, { isFunction: true }, { name, seq }).catch(e => null);
|
||||
const handle = await context.evaluateExpressionHandle(`arg => globalThis['${PageBinding.kController}'].takeBindingHandle(arg)`, { isFunction: true }, { name, seq }).catch(e => null);
|
||||
result = await binding.playwrightFunction({ frame: context.frame, page, context: page.browserContext }, handle);
|
||||
} else {
|
||||
if (!Array.isArray(serializedArgs))
|
||||
@ -881,9 +896,9 @@ export class PageBinding {
|
||||
const args = serializedArgs!.map(a => parseEvaluationResultValue(a));
|
||||
result = await binding.playwrightFunction({ frame: context.frame, page, context: page.browserContext }, ...args);
|
||||
}
|
||||
context.evaluateExpressionHandle(`arg => ${js.accessBindingsController()}.deliverBindingResult(arg)`, { isFunction: true }, { name, seq, result }).catch(e => debugLogger.log('error', e));
|
||||
context.evaluateExpressionHandle(`arg => globalThis['${PageBinding.kController}'].deliverBindingResult(arg)`, { isFunction: true }, { name, seq, result }).catch(e => debugLogger.log('error', e));
|
||||
} catch (error) {
|
||||
context.evaluateExpressionHandle(`arg => ${js.accessBindingsController()}.deliverBindingResult(arg)`, { isFunction: true }, { name, seq, error }).catch(e => debugLogger.log('error', e));
|
||||
context.evaluateExpressionHandle(`arg => globalThis['${PageBinding.kController}'].deliverBindingResult(arg)`, { isFunction: true }, { name, seq, error }).catch(e => debugLogger.log('error', e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,7 +26,6 @@ import { Frame } from '../frames';
|
||||
import { Page } from '../page';
|
||||
import { ThrottledFile } from './throttledFile';
|
||||
import { generateCode } from '../codegen/language';
|
||||
import { prepareGeneratedScript } from '../javascript';
|
||||
|
||||
import type { RegisteredListener } from '../../utils';
|
||||
import type { Language, LanguageGenerator, LanguageGeneratorOptions } from '../codegen/types';
|
||||
@ -147,7 +146,7 @@ export class ContextRecorder extends EventEmitter {
|
||||
await this._context.exposeBinding('__pw_recorderRecordAction', false,
|
||||
(source: BindingSource, action: actions.Action) => this._recordAction(source.frame, action));
|
||||
|
||||
await this._context.extendInjectedScript(prepareGeneratedScript(rawRecorderSource.source));
|
||||
await this._context.extendInjectedScript(rawRecorderSource.source);
|
||||
}
|
||||
|
||||
setEnabled(enabled: boolean) {
|
||||
|
||||
@ -30,8 +30,7 @@ import * as dom from '../dom';
|
||||
import { TargetClosedError } from '../errors';
|
||||
import { helper } from '../helper';
|
||||
import * as network from '../network';
|
||||
import { kPlaywrightBinding } from '../javascript';
|
||||
import { Page } from '../page';
|
||||
import { Page, PageBinding } from '../page';
|
||||
import { getAccessibilityTree } from './wkAccessibility';
|
||||
import { WKSession } from './wkConnection';
|
||||
import { createHandle, WKExecutionContext } from './wkExecutionContext';
|
||||
@ -182,7 +181,7 @@ export class WKPage implements PageDelegate {
|
||||
this._workers.initializeSession(session)
|
||||
];
|
||||
if (this._page.browserContext.needsPlaywrightBinding())
|
||||
promises.push(session.send('Runtime.addBinding', { name: kPlaywrightBinding }));
|
||||
promises.push(session.send('Runtime.addBinding', { name: PageBinding.kBindingName }));
|
||||
if (this._page.needsRequestInterception()) {
|
||||
promises.push(session.send('Network.setInterceptionEnabled', { enabled: true }));
|
||||
promises.push(session.send('Network.setResourceCachingDisabled', { disabled: true }));
|
||||
@ -774,7 +773,7 @@ export class WKPage implements PageDelegate {
|
||||
}
|
||||
|
||||
async exposePlaywrightBinding() {
|
||||
await this._updateState('Runtime.addBinding', { name: kPlaywrightBinding });
|
||||
await this._updateState('Runtime.addBinding', { name: PageBinding.kBindingName });
|
||||
}
|
||||
|
||||
private _calculateBootstrapScript(): string {
|
||||
|
||||
@ -144,9 +144,6 @@ const inlineCSSPlugin = {
|
||||
let content = await fs.promises.readFile(outFileJs, 'utf-8');
|
||||
if (hasExports)
|
||||
content = await replaceEsbuildHeader(content, outFileJs);
|
||||
if (injected.endsWith('utilityScript.ts') && !content.includes('kUtilityScriptIsUnderTest = false')) {
|
||||
throw new Error(`Utility script must include "kUtilityScriptIsUnderTest = false"\n\n${content}`);
|
||||
}
|
||||
const newContent = `export const source = ${JSON.stringify(content)};`;
|
||||
await fs.promises.writeFile(path.join(generatedFolder, baseName.replace('.ts', 'Source.ts')), newContent);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user