New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

sucrase

Package Overview
Dependencies
Maintainers
1
Versions
82
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

sucrase - npm Package Compare versions

Comparing version 1.7.0 to 1.8.0

dist/src/register.d.ts

15

dist/src/augmentTokens.js

@@ -101,2 +101,6 @@ "use strict";

}
else if (this.tokens.matches(["="]) && context === "class") {
this.advance();
this.processRegion([], [], "classFieldExpression", { followingLabels: [";"] });
}
else if (this.startsWithKeyword(["if", "for", "while", "catch"])) {

@@ -116,3 +120,3 @@ // Code of the form TOKEN (...) BLOCK

}
else if (this.startsWithKeyword(["function"])) {
else if (this.startsWithKeyword(["function"]) && context !== "class") {
this.advance();

@@ -332,4 +336,5 @@ if (this.tokens.matches(["name"])) {

this.advance("name");
if (this.tokens.matches(["</>"]) && this.tokens.currentToken().value === "<") {
this.skipBalancedAngleBrackets();
// Skip past any type parameter name or extends declaration.
while (!this.tokens.matches(["{"])) {
this.advance();
}

@@ -391,5 +396,5 @@ this.skipBalancedCode("{", "}");

// Check if there's any indication that we can expand forward, and do so.
if (TokenUtil_1.isTypeBinop(this.tokens.currentToken().type)) {
if (TokenUtil_1.isTypeBinop(this.tokens.currentToken())) {
this.advance();
this.skipTypeExpression();
this.skipTypeExpression(disallowArrow);
}

@@ -396,0 +401,0 @@ else if (this.tokens.matches(["."])) {

@@ -20,2 +20,7 @@ import NameManager from "./NameManager";

preprocessTokens(): void;
/**
* In TypeScript, import statements that only import types should be removed. This does not count
* bare imports.
*/
pruneTypeOnlyImports(): void;
private generateImportReplacements();

@@ -22,0 +27,0 @@ private getFreeIdentifierForPath(path);

@@ -57,2 +57,29 @@ "use strict";

}
/**
* In TypeScript, import statements that only import types should be removed. This does not count
* bare imports.
*/
pruneTypeOnlyImports() {
const nonTypeIdentifiers = new Set();
for (const token of this.tokens.tokens) {
if (token.type.label === "name" &&
token.contextName !== "type" &&
token.contextName !== "import") {
nonTypeIdentifiers.add(token.value);
}
}
for (const [path, importInfo] of this.importInfoByPath.entries()) {
if (importInfo.hasBareImport) {
continue;
}
const names = [
...importInfo.defaultNames,
...importInfo.wildcardNames,
...importInfo.namedImports.map(({ localName }) => localName),
];
if (names.every((name) => !nonTypeIdentifiers.has(name))) {
this.importsToReplace.set(path, "");
}
}
}
generateImportReplacements() {

@@ -145,2 +172,5 @@ for (const [path, importInfo] of this.importInfoByPath.entries()) {

importInfo.namedImports.push(...namedImports);
if (defaultNames.length === 0 && wildcardNames.length === 0 && namedImports.length === 0) {
importInfo.hasBareImport = true;
}
}

@@ -260,2 +290,3 @@ preprocessExportAtIndex(index) {

namedExports: [],
hasBareImport: false,
hasStarExport: false,

@@ -262,0 +293,0 @@ };

@@ -1,2 +0,2 @@

export declare type TokenContext = "block" | "parens" | "brackets" | "object" | "class" | "jsxTag" | "jsxChild" | "jsxExpression" | "templateExpr" | "switchCaseCondition" | "type" | "typeParameter" | "import" | "namedExport";
export declare type TokenContext = "block" | "parens" | "brackets" | "object" | "class" | "classFieldExpression" | "jsxTag" | "jsxChild" | "jsxExpression" | "templateExpr" | "switchCaseCondition" | "type" | "typeParameter" | "import" | "namedExport";
export declare type TokenType = {

@@ -3,0 +3,0 @@ label: string;

@@ -114,2 +114,5 @@ "use strict";

nextToken() {
if (this.tokenIndex === this.tokens.length) {
throw new Error("Unexpectedly reached end of input.");
}
this.tokenIndex++;

@@ -116,0 +119,0 @@ }

@@ -200,3 +200,5 @@ "use strict";

}
else if (this.tokens.matches(["export", "class"])) {
else if (this.tokens.matches(["export", "class"]) ||
// export abstract class
this.tokens.matches(["export", "name", "class"])) {
this.processExportClass();

@@ -306,2 +308,8 @@ }

this.tokens.copyToken();
if (this.tokens.currentToken().contextName === "typeParameter") {
this.tokens.removeInitialToken();
while (this.tokens.currentToken().contextName === "typeParameter") {
this.tokens.removeToken();
}
}
this.tokens.copyExpectedToken("(");

@@ -323,3 +331,6 @@ this.rootTransformer.processBalancedCode();

processExportClass() {
this.tokens.replaceToken("");
this.tokens.removeInitialToken();
if (this.tokens.matchesName("abstract")) {
this.tokens.removeToken();
}
const name = this.rootTransformer.processNamedClass();

@@ -326,0 +337,0 @@ this.tokens.appendCode(` exports.${name} = ${name};`);

@@ -36,3 +36,6 @@ "use strict";

if (transforms.includes("typescript")) {
this.transformers.push(new TypeScriptTransformer_1.default(this, tokens));
if (!transforms.includes("imports")) {
throw new Error("The TypeScript transform without the import transform is not yet supported.");
}
this.transformers.push(new TypeScriptTransformer_1.default(this, tokens, importProcessor));
}

@@ -39,0 +42,0 @@ }

@@ -0,1 +1,2 @@

import ImportProcessor from "../ImportProcessor";
import TokenProcessor from "../TokenProcessor";

@@ -7,3 +8,5 @@ import RootTransformer from "./RootTransformer";

readonly tokens: TokenProcessor;
constructor(rootTransformer: RootTransformer, tokens: TokenProcessor);
readonly importProcessor: ImportProcessor;
constructor(rootTransformer: RootTransformer, tokens: TokenProcessor, importProcessor: ImportProcessor);
preprocess(): void;
process(): boolean;

@@ -15,2 +18,9 @@ /**

isNonNullAssertion(): boolean;
processEnum(): void;
/**
* Rather than try to compute the actual enum values at compile time, we just create variables for
* each one and let everything evaluate at runtime. There's some additional complexity due to
* handling string literal names, including ones that happen to be valid identifiers.
*/
processEnumBody(enumName: string): void;
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const isIdentifier_1 = require("../util/isIdentifier");
const Transformer_1 = require("./Transformer");
class TypeScriptTransformer extends Transformer_1.default {
constructor(rootTransformer, tokens) {
constructor(rootTransformer, tokens, importProcessor) {
super();
this.rootTransformer = rootTransformer;
this.tokens = tokens;
this.importProcessor = importProcessor;
}
preprocess() {
this.importProcessor.pruneTypeOnlyImports();
}
process() {

@@ -46,2 +51,6 @@ // We need to handle all classes specially in order to remove `implements`.

}
if (this.tokens.matchesName("enum")) {
this.processEnum();
return true;
}
return false;

@@ -88,3 +97,93 @@ }

}
processEnum() {
this.tokens.removeInitialToken();
const enumName = this.tokens.currentToken().value;
this.tokens.removeToken();
this.tokens.appendCode(`var ${enumName}; (function (${enumName})`);
this.tokens.copyExpectedToken("{");
this.processEnumBody(enumName);
this.tokens.copyExpectedToken("}");
this.tokens.appendCode(`)(${enumName} || (${enumName} = {}));`);
}
/**
* Rather than try to compute the actual enum values at compile time, we just create variables for
* each one and let everything evaluate at runtime. There's some additional complexity due to
* handling string literal names, including ones that happen to be valid identifiers.
*/
processEnumBody(enumName) {
let isPreviousValidIdentifier = false;
let lastValueReference = null;
while (true) {
if (this.tokens.matches(["}"])) {
break;
}
const nameToken = this.tokens.currentToken();
let name;
let isValidIdentifier;
let nameStringCode;
if (nameToken.type.label === "name") {
name = nameToken.value;
isValidIdentifier = true;
nameStringCode = `"${name}"`;
}
else if (nameToken.type.label === "string") {
name = nameToken.value;
isValidIdentifier = isIdentifier_1.default(name);
nameStringCode = this.tokens.code.slice(nameToken.start, nameToken.end);
}
else {
throw new Error("Expected name or string at beginning of enum element.");
}
this.tokens.removeInitialToken();
let valueIsString;
let valueCode;
if (this.tokens.matches(["="])) {
const contextStartIndex = this.tokens.currentToken().contextStartIndex;
this.tokens.removeToken();
if (this.tokens.matches(["string", ","]) || this.tokens.matches(["string", "}"])) {
valueIsString = true;
}
const startToken = this.tokens.currentToken();
while (!this.tokens.matchesContextEnd(",", contextStartIndex) &&
!this.tokens.matchesContextEnd("}", contextStartIndex)) {
this.tokens.removeToken();
}
valueCode = this.tokens.code.slice(startToken.start, this.tokens.tokenAtRelativeIndex(-1).end);
}
else {
valueIsString = false;
if (lastValueReference != null) {
if (isPreviousValidIdentifier) {
valueCode = `${lastValueReference} + 1`;
}
else {
valueCode = `(${lastValueReference}) + 1`;
}
}
else {
valueCode = "0";
}
}
if (this.tokens.matches([","])) {
this.tokens.removeToken();
}
let valueReference;
if (isValidIdentifier) {
this.tokens.appendCode(`const ${name} = ${valueCode}; `);
valueReference = name;
}
else {
valueReference = valueCode;
}
if (valueIsString) {
this.tokens.appendCode(`${enumName}[${nameStringCode}] = ${valueReference};`);
}
else {
this.tokens.appendCode(`${enumName}[${enumName}[${nameStringCode}] = ${valueReference}] = ${nameStringCode};`);
}
lastValueReference = valueReference;
isPreviousValidIdentifier = isValidIdentifier;
}
}
}
exports.default = TypeScriptTransformer;

@@ -28,7 +28,3 @@ "use strict";

}
const nameToken = tokens.currentToken();
if (nameToken.type.label !== "name") {
throw new Error("Expected name for class method/field.");
}
tokens.nextToken();
const nameCode = getNameCode(tokens);
if (tokens.matches(["</>"]) || tokens.matches(["("])) {

@@ -57,3 +53,3 @@ // This is a method, so just skip to the close-brace at the end.

const expressionCode = tokens.code.slice(assignExpressionStart, tokens.currentToken().start);
classInitializers.push(`this.${nameToken.value} = ${expressionCode}`);
classInitializers.push(`this${nameCode} = ${expressionCode}`);
}

@@ -112,6 +108,36 @@ tokens.nextToken();

}
/**
* Determine if this is any token that can go before the name in a method/field.
*/
function isAccessModifier(tokens) {
return ((tokens.matches(["name"]) &&
["public", "protected", "private", "readonly"].includes(tokens.currentToken().value)) ||
["public", "protected", "private", "readonly", "static", "async"].includes(tokens.currentToken().value)) ||
tokens.matches(["+/-"]));
}
/**
* The next token or set of tokens is either an identifier or an expression in square brackets, for
* a method or field name. Get the code that would follow `this` to access this value. Note that a
* more correct implementation would precompute computed field and method names, but that's harder,
* and TypeScript doesn't do it, so we won't either.
*/
function getNameCode(tokens) {
if (tokens.matches(["["])) {
const startToken = tokens.currentToken();
while (!tokens.matchesContextEnd("]", startToken.contextStartIndex)) {
tokens.nextToken();
}
const endToken = tokens.currentToken();
tokens.nextToken();
return tokens.code.slice(startToken.start, endToken.end);
}
else {
const nameToken = tokens.currentToken();
tokens.nextToken();
if (nameToken.type.label === "string" || nameToken.type.label === "num") {
return `[${tokens.code.slice(nameToken.start, nameToken.end)}]`;
}
else {
return `.${nameToken.value}`;
}
}
}

@@ -1,2 +0,2 @@

import { TokenType } from "../TokenProcessor";
import { Token, TokenType } from "../TokenProcessor";
/**

@@ -8,2 +8,2 @@ * An "atom" in this context is a token that is an expression all by itself,

export declare function isTypeExpressionPrefix(tokenType: TokenType): boolean;
export declare function isTypeBinop(tokenType: TokenType): boolean;
export declare function isTypeBinop(token: Token): boolean;

@@ -17,5 +17,5 @@ "use strict";

exports.isTypeExpressionPrefix = isTypeExpressionPrefix;
function isTypeBinop(tokenType) {
return ["|", "&"].includes(tokenType.label);
function isTypeBinop(token) {
return (["|", "&"].includes(token.type.label) || (token.type.label === "name" && token.value === "is"));
}
exports.isTypeBinop = isTypeBinop;

@@ -780,2 +780,3 @@ "use strict";

this.expectRelational("<");
this.state.tokens[this.state.tokens.length - 1].type = _types.types.typeParameterStart;
const typeArguments = this.tsParseDelimitedList("TypeParametersOrArguments", this.tsParseType.bind(this));

@@ -1292,2 +1293,3 @@ this.expectRelational(">");

const typeParameters = this.tsTryParseAndCatch(() => {
this.state.type = _types.types.typeParameterStart;
const args = this.tsParseTypeArguments();

@@ -1294,0 +1296,0 @@ if (!this.match(_types.types.parenL)) this.unexpected();

{
"name": "sucrase",
"version": "1.7.0",
"version": "1.8.0",
"description": "Super-fast alternative to Babel for when you can target modern JS runtimes",

@@ -15,3 +15,4 @@ "author": "Alan Pierce <alangpierce@gmail.com>",

"bin",
"dist"
"dist",
"register"
],

@@ -21,3 +22,3 @@ "scripts": {

"self-build": "script/self-build",
"self-test": "OUT_DIR=self-build yarn self-build && mocha self-build/test --opts test/mocha-self-test.opts",
"self-test": "mocha test/**/*.ts --opts test/mocha-self-test.opts",
"benchmark": "node ./build/benchmark/benchmark.js",

@@ -75,2 +76,3 @@ "lint": "eslint './src/**/*.ts' && tslint -p .",

"mz": "^2.7.0",
"pirates": "^3.0.2",
"tslib": "^1.7.1"

@@ -77,0 +79,0 @@ },

@@ -26,14 +26,33 @@ # Sucrase

(13K lines of code, flow, imports).
* [decaffeinate](https://github.com/decaffeinate/decaffeinate) and its
sub-projects [decaffeinate-parser](https://github.com/decaffeinate/decaffeinate-parser)
and [coffee-lex](https://github.com/decaffeinate/coffee-lex)
(38K lines of code, typescript).
## Usage
Currently Sucrase ships with a simple CLI and can be called from JavaScript
directly:
Installation:
```
yarn add sucrase # Or npm install sucrase
```
Run on a directory:
```
sucrase ./srcDir --transforms imports,flow -d ./outDir
```
Register a require hook with some [reasonable defaults](src/register.ts):
```js
// Register just one extension.
import "sucrase/register/ts";
// Or register all at once.
import "sucrase/register";
```
Call from JS directly:
```js
import {transform} from "sucrase";

@@ -43,2 +62,17 @@ const compiledCode = transform(code, {transforms: ["imports", "flow"]});

## Current assumptions
Sucrase currently makes some simplifying assumptions about your code, which are
often seen as good practice anyway:
* It assumes that imported names are never shadowed by variable names. If you
use ESLint, [no-shadow](https://eslint.org/docs/rules/no-shadow) and TSLint
[no-shadowed-variable](https://palantir.github.io/tslint/rules/no-shadowed-variable/)
can enforce this rule.
* It assumes your code uses semicolons rather than relying on automatic
semicolon insertion. ESLint [semi](https://eslint.org/docs/rules/semi) and
TSLint [semicolon](https://palantir.github.io/tslint/rules/semicolon/) can
enforce and autofix this rule.
* It assumes you don't have variables with the same name as non-reserved
keywords, like `as`. Some steps may run into trouble if you do.
## Supported transforms

@@ -62,6 +96,7 @@

* Does not handle enums yet.
* Does not handle static class fields yet.
* Does not remove type-only imports.
* Some syntax, like `declare`, is not yet supported.
* There are minor differences in import semantics. For example, TypeScript
allows you to import a function `f` from a CommonJS module using
`import * as f from "f";`, but Babel and Sucrase do not.
* Only removes types; no type checking.

@@ -103,11 +138,5 @@

* Assumes that there are no variables shadowing imported names. Any such
variables will be incorrectly transformed. If you use ESLint, the
[no-shadow](https://eslint.org/docs/rules/no-shadow) rule should avoid this
issue.
* Complex assignments to exported names do not update the live bindings
properly. For example, after `export let a = 1;`, `a = 2;` works, but
`[a] = [3];` will not cause imported usages of `a` to update.
* The code for handling object shorthand does not take ASI into account, so
there may be rare bugs if you omit semicolons.
* Imports are not hoisted to the top of the file.

@@ -143,3 +172,4 @@

Sucrase bypasses most of these steps, and works like this:
1. Tokenize the input source code into a token stream using Babel's tokenizer.
1. Tokenize the input source code into a token stream using a fork of Babel's
parser.
2. Scan through the tokens, computing preliminary information like all

@@ -181,4 +211,4 @@ imported/exported names and additional info on the role of each token.

* Fork the Babylon lexer and simplify it to the essentials of what's needed
for this project, with a focus on performance.
* Simplify the Babylon fork to the essentials of what's needed for this project,
with a focus on performance.
* Rewrite the code to be valid [AssemblyScript](https://github.com/AssemblyScript/assemblyscript),

@@ -189,3 +219,4 @@ which will allow it to be compiled to wasm and hopefully improve performance

be matched as part of code transformation.
* Explore more optimizations, like reducing the number of passes.
* Explore more ad-hoc optimizations, like reducing the number of passes,
avoiding any unneeded work, and rewriting slow spots.

@@ -192,0 +223,0 @@ ### Correctness and stability

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