Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ts-patch

Package Overview
Dependencies
Maintainers
1
Versions
46
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ts-patch - npm Package Compare versions

Comparing version 3.0.2 to 3.1.0

14

CHANGELOG.md

@@ -5,2 +5,16 @@ # Changelog

## [3.1.0](https://github.com/nonara/ts-patch/compare/v3.0.2...v3.1.0) (2023-12-05)
### Features
* Added Plugin Package Configuration + parseAllJsDoc (closes [#134](https://github.com/nonara/ts-patch/issues/134) closes [#133](https://github.com/nonara/ts-patch/issues/133)) ([15570d0](https://github.com/nonara/ts-patch/commit/15570d05e422dd02635eb3c63dc6b3a036cb543a))
* Added support for chaining transformers in single factory (closes [#122](https://github.com/nonara/ts-patch/issues/122) closes [#120](https://github.com/nonara/ts-patch/issues/120)) ([aabf389](https://github.com/nonara/ts-patch/commit/aabf3894a610047fade6d2d9fb9949f17afe09df))
### Bug Fixes
* TsExtras 'ts' type registering as 'any' (fixes [#127](https://github.com/nonara/ts-patch/issues/127)) ([069411e](https://github.com/nonara/ts-patch/commit/069411e49819aa87c880c7c5ff2ab04ecb68eea3))
* tsp.currentLibrary was not set (detail) ([24d8031](https://github.com/nonara/ts-patch/commit/24d8031bafcd6483fa762ed9ad8437c4a4070093))
### [3.0.2](https://github.com/nonara/ts-patch/compare/v3.0.1...v3.0.2) (2023-07-20)

@@ -7,0 +21,0 @@

4

package.json
{
"name": "ts-patch",
"version": "3.0.2",
"version": "3.1.0",
"description": "Patch typescript to support custom transformers in tsconfig.json",

@@ -33,3 +33,3 @@ "main": "./index.js",

"resolve": "^1.22.2",
"semver": "^7.3.8",
"semver": "^7.5.4",
"strip-ansi": "^6.0.1"

@@ -36,0 +36,0 @@ },

@@ -93,5 +93,7 @@ "use strict";

/* Get JS */
const libraryName = tsModule.moduleName.replace(/\.js$/, '');
const patchDetail = patch_detail_1.PatchDetail.fromModule(tsModule, printedJs);
const js = patchDetail.toHeader() + '\n' +
jsPatchSrc + '\n' +
`tsp.currentLibrary = '${libraryName}';\n` +
printedJs;

@@ -98,0 +100,0 @@ return { dts, js };

/**
* NOTE: This file is used during the build process for patch as well
*/
import type ts from 'typescript';
import type * as ts from 'typescript';
export interface PluginConfig {

@@ -54,5 +54,5 @@ [x: string]: any;

export interface TransformerBasePlugin {
before?: ts.TransformerFactory<ts.SourceFile>;
after?: ts.TransformerFactory<ts.SourceFile>;
afterDeclarations?: ts.TransformerFactory<ts.SourceFile | ts.Bundle>;
before?: ts.TransformerFactory<ts.SourceFile> | ts.TransformerFactory<ts.SourceFile>[];
after?: ts.TransformerFactory<ts.SourceFile> | ts.TransformerFactory<ts.SourceFile>[];
afterDeclarations?: ts.TransformerFactory<ts.SourceFile | ts.Bundle> | ts.TransformerFactory<ts.SourceFile | ts.Bundle>[];
}

@@ -86,1 +86,12 @@ export type DiagnosticMap = WeakMap<ts.Program, ts.Diagnostic[]>;

export type RawPattern = (context: ts.TransformationContext, program: ts.Program, config: {}) => ts.Transformer<ts.SourceFile>;
export interface PluginPackageConfig {
tscOptions?: {
/**
* Sets the JSDocParsingMode to ParseAll
*
* @see https://devblogs.microsoft.com/typescript/announcing-typescript-5-3/#optimizations-by-skipping-jsdoc-parsing
* @default false
*/
parseAllJsDoc?: boolean;
};
}

@@ -43,3 +43,5 @@ [![npm version](https://badge.fury.io/js/ts-patch.svg)](https://badge.fury.io/js/ts-patch)

* [Configuring Program Transformers](#configuring-program-transformers)
* [Example Program Transformer](#example-program-transformer)
* [Program Transformer Example](#program-transformer-example)
* [Plugin Package Configuration](#plugin-package-configuration)
* [Example](#example)
* [Resources](#resources)

@@ -109,3 +111,3 @@ * [Recommended Reading](#recommended-reading)

{ "transform": "trans-with-mapping", "resolvePathAliases": true },
{ "transform": "esm-transformer, "isEsm": true },
{ "transform": "esm-transformer", "isEsm": true },

@@ -138,2 +140,4 @@ // Program Transformer

For an overview of the typescript compiler (such as what a `SourceFile` and `Program` is) see: [Typescript Compiler Notes](https://github.com/microsoft/TypeScript-Compiler-Notes).
## Source Transformers

@@ -188,4 +192,6 @@

[`{ transform: "typia/lib/transform" }`](https://github.com/samchon/typia)
[`{ transform: "typia/lib/transform" }`](https://github.com/samchon/typia) ([💻playground](https://typia.io/playground))
[`{ transform: "@nestia/core/lib/transform" }`](https://github.com/samchon/nestia)
### Altering Diagnostics

@@ -234,3 +240,3 @@

### Example Program Transformer
### Program Transformer Example
```TypeScript

@@ -263,2 +269,29 @@ /**

**Live Examples**:
[`{ transform: "@typescript-virtual-barrel/compiler-plugin", transformProgram: true }`](https://github.com/zaguiini/typescript-virtual-barrel)
## Plugin Package Configuration
The plugin package configuration allows you to specify custom options for your TypeScript plugin.
This configuration is defined in the `package.json` of your plugin under the `tsp` property.
An example use case is enabling `parseAllJsDoc` if you require full JSDoc parsing in tsc for your transformer in TS v5.3+. (see: [5.3 JSDoc parsing changes](https://devblogs.microsoft.com/typescript/announcing-typescript-5-3/#optimizations-by-skipping-jsdoc-parsing))
For all available options, see the `PluginPackageConfig` type in [plugin-types.ts](https://github.com/nonara/ts-patch/blob/master/projects/core/shared/plugin-types.ts)
### Example
```jsonc
{
"name": "your-plugin-name",
"version": "1.0.0",
"tsp": {
"tscOptions": {
"parseAllJsDoc": true
}
}
}
```
## Resources

@@ -315,3 +348,3 @@

If you're interested in helping and have a _high level_ of skill with the TS compiler API, please reach out!
If you're interested in helping and are knowledgeable with the TS compiler codebase, feel free to reach out!

@@ -318,0 +351,0 @@ # License

@@ -20,5 +20,5 @@ declare namespace ts {

interface TransformerBasePlugin {
before?: ts.TransformerFactory<ts.SourceFile>;
after?: ts.TransformerFactory<ts.SourceFile>;
afterDeclarations?: ts.TransformerFactory<ts.SourceFile | ts.Bundle>;
before?: ts.TransformerFactory<ts.SourceFile> | ts.TransformerFactory<ts.SourceFile>[];
after?: ts.TransformerFactory<ts.SourceFile> | ts.TransformerFactory<ts.SourceFile>[];
afterDeclarations?: ts.TransformerFactory<ts.SourceFile | ts.Bundle> | ts.TransformerFactory<ts.SourceFile | ts.Bundle>[];
}

@@ -43,2 +43,7 @@ type DiagnosticMap = WeakMap<ts.Program, ts.Diagnostic[]>;

type RawPattern = (context: ts.TransformationContext, program: ts.Program, config: {}) => ts.Transformer<ts.SourceFile>;
interface PluginPackageConfig {
tscOptions?: {
parseAllJsDoc?: boolean;
};
}
}

@@ -45,0 +50,0 @@ declare namespace ts {

@@ -127,8 +127,3 @@ var tsp = (function () {

const crypto = require("crypto");
function validateConfigs(configs) {
for (const config of configs)
if (!config.name && !config.transform)
throw new tsp.TsPatchError("tsconfig.json plugins error: transform must be present");
}
function createTransformerFromPattern(opt) {
function createTransformersFromPattern(opt) {
const { factory, config, program, ls, registerConfig } = opt;

@@ -138,3 +133,5 @@ const { transform, after, afterDeclarations, name, type, transformProgram, ...cleanConfig } = config;

throw new tsp.TsPatchError("Not a valid config entry: \"transform\" key not found");
const transformerKind = after ? "after" : afterDeclarations ? "afterDeclarations" : "before";
const transformerKind = after ? "after" :
afterDeclarations ? "afterDeclarations" :
"before";
let pluginFactoryResult;

@@ -173,20 +170,33 @@ switch (config.type) {

}
let transformerFactory;
let transformerFactories;
switch (typeof pluginFactoryResult) {
case "function":
transformerFactory = pluginFactoryResult;
transformerFactories = [pluginFactoryResult];
break;
case "object":
transformerFactory = pluginFactoryResult[transformerKind];
break;
const factoryOrFactories = pluginFactoryResult[transformerKind];
if (typeof factoryOrFactories === "function") {
transformerFactories = [pluginFactoryResult[transformerKind]];
break;
}
else if (Array.isArray(factoryOrFactories)) {
transformerFactories = [...factoryOrFactories];
break;
}
default:
throw new tsp.TsPatchError(`Invalid plugin result: expected a function or an object with a '${transformerKind}' property`);
}
if (!transformerFactory || typeof transformerFactory !== "function")
throw new tsp.TsPatchError(`Invalid plugin entry point! Expected a transformer factory function or an object with a '${transformerKind}' property`);
const wrapper = wrapTransformer(transformerFactory, registerConfig, true);
const wrappedFactories = [];
for (const transformerFactory of transformerFactories) {
if (!transformerFactory || typeof transformerFactory !== "function")
throw new tsp.TsPatchError(`Invalid plugin entry point! Expected a transformer factory function or an object with a '${transformerKind}' property`);
const wrapper = wrapTransformerFactory(transformerFactory, registerConfig, true);
wrappedFactories.push(wrapper);
}
const res = {
[transformerKind]: wrapper
[transformerKind]: wrappedFactories
};
return res;
}
function wrapTransformer(transformerFn, requireConfig, wrapInnerFunction) {
function wrapTransformerFactory(transformerFn, requireConfig, wrapInnerFunction) {
const wrapper = function tspWrappedFactory(...args) {

@@ -203,3 +213,3 @@ let res;

throw new tsp.TsPatchError("Invalid plugin: expected a function");
res = wrapTransformer(resFn, requireConfig, false);
res = wrapTransformerFactory(resFn, requireConfig, false);
}

@@ -215,6 +225,9 @@ }

class PluginCreator {
constructor(configs, resolveBaseDir = process.cwd()) {
constructor(configs, options) {
this.plugins = [];
this.configs = configs;
this.resolveBaseDir = resolveBaseDir;
validateConfigs(configs);
this.options = options;
const { resolveBaseDir } = options;
this.plugins = configs.map(config => new tsp.TspPlugin(config, { resolveBaseDir }));
this.needsTscJsDocParsing = this.plugins.some(plugin => plugin.packageConfig?.tscOptions?.parseAllJsDoc === true);
}

@@ -231,13 +244,14 @@ mergeTransformers(into, source) {

}
createTransformers(params, customTransformers) {
createSourceTransformers(params, customTransformers) {
const transformers = { before: [], after: [], afterDeclarations: [] };
const [ls, program] = ("ls" in params) ? [params.ls, params.ls.getProgram()] : [void 0, params.program];
for (const config of this.configs) {
if (!config.transform || config.transformProgram)
for (const plugin of this.plugins) {
if (plugin.kind !== "SourceTransformer")
continue;
const resolvedFactory = tsp.resolveFactory(this, config);
if (!resolvedFactory)
const { config } = plugin;
const createFactoryResult = plugin.createFactory();
if (!createFactoryResult)
continue;
const { factory, registerConfig } = resolvedFactory;
this.mergeTransformers(transformers, createTransformerFromPattern({
const { factory, registerConfig } = createFactoryResult;
this.mergeTransformers(transformers, createTransformersFromPattern({
factory: factory,

@@ -254,12 +268,13 @@ registerConfig,

}
getProgramTransformers() {
createProgramTransformers() {
const res = new Map();
for (const config of this.configs) {
if (!config.transform || !config.transformProgram)
for (const plugin of this.plugins) {
if (plugin.kind !== "ProgramTransformer")
continue;
const resolvedFactory = tsp.resolveFactory(this, config);
if (resolvedFactory === undefined)
const { config } = plugin;
const createFactoryResult = plugin.createFactory();
if (createFactoryResult === undefined)
continue;
const { registerConfig } = resolvedFactory;
const factory = wrapTransformer(resolvedFactory.factory, registerConfig, false);
const { registerConfig, factory: unwrappedFactory } = createFactoryResult;
const factory = wrapTransformerFactory(unwrappedFactory, registerConfig, false);
const transformerKey = crypto

@@ -279,2 +294,118 @@ .createHash("md5")

const path = require("path");
const fs = require("fs");
const requireStack = [];
function getPackagePath(entryFilePath) {
let currentDir = path.dirname(entryFilePath);
const seenPaths = new Set();
while (currentDir !== path.parse(currentDir).root) {
if (seenPaths.has(currentDir))
return undefined;
seenPaths.add(currentDir);
const potentialPkgPath = path.join(currentDir, "package.json");
if (fs.existsSync(potentialPkgPath))
return potentialPkgPath;
currentDir = path.resolve(currentDir, "..");
}
return undefined;
}
class TspPlugin {
constructor(config, createOptions) {
this.config = { ...config };
this.validateConfig();
this._createOptions = createOptions;
this.importKey = config.import || "default";
this.kind = config.transformProgram === true ? "ProgramTransformer" : "SourceTransformer";
const { resolveBaseDir } = createOptions;
const configTransformValue = config.transform;
this.tsConfigPath = config.tsConfig && path.resolve(resolveBaseDir, config.tsConfig);
const entryFilePath = require.resolve(configTransformValue, { paths: [resolveBaseDir] });
this.entryFilePath = entryFilePath;
let pluginPackageConfig;
const modulePackagePath = getPackagePath(entryFilePath);
if (modulePackagePath) {
const modulePkgJsonContent = fs.readFileSync(modulePackagePath, "utf8");
const modulePkgJson = JSON.parse(modulePkgJsonContent);
pluginPackageConfig = modulePkgJson.tsp;
if (pluginPackageConfig === null || typeof pluginPackageConfig !== "object")
pluginPackageConfig = undefined;
}
this.packageConfig = pluginPackageConfig;
}
validateConfig() {
const { config } = this;
const configTransformValue = config.transform;
if (!configTransformValue)
throw new tsp.TsPatchError(`Invalid plugin config: missing "transform" value`);
if (config.resolvePathAliases && !config.tsConfig) {
console.warn(`[ts-patch] Warning: resolvePathAliases needs a tsConfig value pointing to a tsconfig.json for transformer" ${configTransformValue}.`);
}
}
createFactory() {
const { entryFilePath, config, tsConfigPath, importKey } = this;
const configTransformValue = config.transform;
if (requireStack.includes(entryFilePath))
return;
requireStack.push(entryFilePath);
let isEsm = config.isEsm;
if (isEsm == null) {
const impliedModuleFormat = tsp.tsShim.getImpliedNodeFormatForFile(entryFilePath, undefined, tsp.tsShim.sys, { moduleResolution: tsp.tsShim.ModuleResolutionKind.Node16 });
isEsm = impliedModuleFormat === tsp.tsShim.ModuleKind.ESNext;
}
const isTs = configTransformValue.match(/\.[mc]?ts$/) != null;
const registerConfig = {
isTs,
isEsm,
tsConfig: tsConfigPath,
pluginConfig: config
};
tsp.registerPlugin(registerConfig);
try {
const commonjsModule = loadEntryFile();
const factoryModule = (typeof commonjsModule === "function") ? { default: commonjsModule } : commonjsModule;
const factory = factoryModule[importKey];
if (!factory)
throw new tsp.TsPatchError(`tsconfig.json > plugins: "${configTransformValue}" does not have an export "${importKey}": ` +
require("util").inspect(factoryModule));
if (typeof factory !== "function") {
throw new tsp.TsPatchError(`tsconfig.json > plugins: "${configTransformValue}" export "${importKey}" is not a plugin: ` +
require("util").inspect(factory));
}
return {
factory,
registerConfig: registerConfig
};
}
finally {
requireStack.pop();
tsp.unregisterPlugin();
}
function loadEntryFile() {
let res;
try {
res = require(entryFilePath);
}
catch (e) {
if (e.code === "ERR_REQUIRE_ESM") {
if (!registerConfig.isEsm) {
tsp.unregisterPlugin();
registerConfig.isEsm = true;
tsp.registerPlugin(registerConfig);
return loadEntryFile();
}
else {
throw new tsp.TsPatchError(`Cannot load ESM transformer "${configTransformValue}" from "${entryFilePath}". Please file a bug report`);
}
}
else
throw e;
}
return res;
}
}
}
tsp.TspPlugin = TspPlugin;
})(tsp || (tsp = {}));
var tsp;
(function (tsp) {
const path = require("path");
let configStack = [];

@@ -367,75 +498,2 @@ function getTsNode() {

(function (tsp) {
const path = require("path");
const requireStack = [];
function resolveFactory(pluginCreator, pluginConfig) {
const tsConfig = pluginConfig.tsConfig && path.resolve(pluginCreator.resolveBaseDir, pluginConfig.tsConfig);
const transform = pluginConfig.transform;
const importKey = pluginConfig.import || "default";
const transformerPath = require.resolve(transform, { paths: [pluginCreator.resolveBaseDir] });
if (pluginConfig.resolvePathAliases && !tsConfig) {
console.warn(`[ts-patch] Warning: resolvePathAliases needs a tsConfig value pointing to a tsconfig.json for transformer" ${transform}.`);
}
if (requireStack.includes(transformerPath))
return;
requireStack.push(transformerPath);
let isEsm = pluginConfig.isEsm;
if (isEsm == null) {
const impliedModuleFormat = tsp.tsShim.getImpliedNodeFormatForFile(transformerPath, undefined, tsp.tsShim.sys, { moduleResolution: tsp.tsShim.ModuleResolutionKind.Node16 });
isEsm = impliedModuleFormat === tsp.tsShim.ModuleKind.ESNext;
}
const isTs = transform.match(/\.[mc]?ts$/) != null;
const registerConfig = {
isTs,
isEsm,
tsConfig: tsConfig,
pluginConfig
};
tsp.registerPlugin(registerConfig);
try {
const commonjsModule = loadPlugin();
const factoryModule = (typeof commonjsModule === "function") ? { default: commonjsModule } : commonjsModule;
const factory = factoryModule[importKey];
if (!factory)
throw new tsp.TsPatchError(`tsconfig.json > plugins: "${transform}" does not have an export "${importKey}": ` +
require("util").inspect(factoryModule));
if (typeof factory !== "function") {
throw new tsp.TsPatchError(`tsconfig.json > plugins: "${transform}" export "${importKey}" is not a plugin: ` +
require("util").inspect(factory));
}
return {
factory,
registerConfig: registerConfig,
};
}
finally {
requireStack.pop();
tsp.unregisterPlugin();
}
function loadPlugin() {
let res;
try {
res = require(transformerPath);
}
catch (e) {
if (e.code === "ERR_REQUIRE_ESM") {
if (!registerConfig.isEsm) {
tsp.unregisterPlugin();
registerConfig.isEsm = true;
tsp.registerPlugin(registerConfig);
return loadPlugin();
}
else {
throw new tsp.TsPatchError(`Cannot load ESM transformer "${transform}" from "${transformerPath}". Please file a bug report`);
}
}
else
throw e;
}
return res;
}
}
tsp.resolveFactory = resolveFactory;
})(tsp || (tsp = {}));
var tsp;
(function (tsp) {
const activeProgramTransformers = new Set();

@@ -499,8 +557,11 @@ const { dirname } = require("path");

}
const plugins = preparePluginsFromCompilerOptions(options.plugins);
const pluginCreator = new tsp.PluginCreator(plugins, { resolveBaseDir: projectConfig.projectDir ?? process.cwd() });
if (tsp.currentLibrary === "tsc" && pluginCreator.needsTscJsDocParsing) {
host.jsDocParsingMode = tsp.tsShim.JSDocParsingMode.ParseAll;
}
let program = createOpts ?
tsp.tsShim.originalCreateProgram(createOpts) :
tsp.tsShim.originalCreateProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics);
const plugins = preparePluginsFromCompilerOptions(options.plugins);
const pluginCreator = new tsp.PluginCreator(plugins, projectConfig.projectDir ?? process.cwd());
const programTransformers = pluginCreator.getProgramTransformers();
const programTransformers = pluginCreator.createProgramTransformers();
for (const [transformerKey, [programTransformer, config]] of programTransformers) {

@@ -520,3 +581,3 @@ if (activeProgramTransformers.has(transformerKey))

function newEmit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers, ...additionalArgs) {
const transformers = pluginCreator.createTransformers({ program }, customTransformers);
const transformers = pluginCreator.createSourceTransformers({ program }, customTransformers);
const result = program.originalEmit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, transformers, ...additionalArgs);

@@ -523,0 +584,0 @@ for (const diagnostic of tsp.diagnosticMap.get(program) || [])

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc