Skip to content

Commit 104cfbd

Browse files
feat: add ts-source-parser package (#218)
* feat: create TsSourceParser * docs: create doc * test: add CI for ts-source-parser * fix: blank line typo * fix: add workspace to root package.json * fix: name CI -> TsSourceParser CI * fix:add --workspace flag to test run * fix: package.json indentation * fix: add last blank line package.json * fix: add --workspace CI run test flag to other workspaces
1 parent f4b47c7 commit 104cfbd

File tree

11 files changed

+235
-6
lines changed

11 files changed

+235
-6
lines changed

.github/workflows/estree-ast-utils.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@ jobs:
3838
with:
3939
node-version: ${{ matrix.node-version }}
4040
- run: npm install
41-
- run: npm test
41+
- run: npm run test --workspace=workspaces/estree-ast-utils

.github/workflows/sec-literal.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,5 @@ jobs:
2323
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
2424
with:
2525
node-version: ${{ matrix.node-version }}
26-
- name: Install dependencies
27-
run: npm install
28-
- name: Run tests
29-
run: npm run test
26+
- run: npm install
27+
- run: npm run test --workspace=workspaces/sec-literal
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3+
4+
name: TsSourceParser CI
5+
6+
on:
7+
push:
8+
branches:
9+
- main
10+
paths:
11+
- workspaces/ts-source-parser/**
12+
pull_request:
13+
paths:
14+
- workspaces/ts-source-parser/**
15+
16+
permissions:
17+
contents: read
18+
19+
jobs:
20+
build:
21+
22+
runs-on: ubuntu-latest
23+
24+
strategy:
25+
matrix:
26+
node-version: [18.x, 20.x]
27+
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
28+
29+
steps:
30+
- name: Harden Runner
31+
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
32+
with:
33+
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
34+
35+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
36+
- name: Use Node.js ${{ matrix.node-version }}
37+
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
38+
with:
39+
node-version: ${{ matrix.node-version }}
40+
- run: npm install
41+
- run: npm run test --workspace=workspaces/ts-source-parser

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
},
2121
"workspaces": [
2222
"workspaces/estree-ast-utils",
23-
"workspaces/sec-literal"
23+
"workspaces/sec-literal",
24+
"workspaces/ts-source-parser"
2425
],
2526
"keywords": [
2627
"ast",

workspaces/ts-source-parser/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 NodeSecure
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

workspaces/ts-source-parser/README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# ts-source-parser
2+
[![version](https://img.shields.io/github/package-json/v/NodeSecure/js-x-ray?filename=workspaces%2Fts-source-parser%2Fpackage.json&style=for-the-badge)](https://www.npmjs.com/package/@nodesecure/ts-source-parser)
3+
[![maintained](https://img.shields.io/badge/Maintained%3F-yes-green.svg?style=for-the-badge)](https://github.com/NodeSecure/js-x-ray/blob/master/workspaces/ts-source-parser/graphs/commit-activity)
4+
[![OpenSSF
5+
Scorecard](https://api.securityscorecards.dev/projects/github.com/NodeSecure/js-x-ray/badge?style=for-the-badge)](https://api.securityscorecards.dev/projects/github.com/NodeSecure/js-x-ray)
6+
[![mit](https://img.shields.io/github/license/NodeSecure/js-x-ray?style=for-the-badge)](https://github.com/NodeSecure/js-x-ray/blob/master/workspaces/ts-source-parser/LICENSE)
7+
[![build](https://img.shields.io/github/actions/workflow/status/NodeSecure/js-x-ray/ts-source-parser.yml?style=for-the-badge)](https://github.com/NodeSecure/js-x-ray/actions?query=workflow%3A%22sec+literal+CI%22)
8+
9+
This package provide a TypeScript source parser for the `@nodesecure/js-x-ray` project.
10+
11+
## Getting Started
12+
13+
This package is available in the Node Package Repository and can be easily installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or [yarn](https://yarnpkg.com).
14+
15+
```bash
16+
$ npm i @nodesecure/ts-source-parser
17+
# or
18+
$ yarn add @nodesecure/ts-source-parser
19+
```
20+
21+
## Usage example
22+
```js
23+
import { TsSourceParser } from "@nodesecure/ts-source-parser";
24+
25+
const parser = new TsSourceParser();
26+
const body = parser.parse("const x: number = 5;");
27+
28+
console.log(body);
29+
```
30+
31+
## Usage with `js-x-ray`
32+
```js
33+
import { runASTAnalysis } from "@nodesecure/js-x-ray";
34+
import { readFileSync } from "node:fs";
35+
36+
const { warnings, dependencies } = runASTAnalysis(
37+
readFileSync("./file.ts", "utf-8"),
38+
{ customParser: new TsSourceParser() }
39+
);
40+
41+
console.log(dependencies);
42+
console.dir(warnings, { depth: null });
43+
```
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { tsParsingOptions } from "./src/TsSourceParser";
2+
3+
declare class TsSourceParser {
4+
parse(
5+
code: string,
6+
options = tsParsingOptions,
7+
): TSESTree.Program;
8+
}

workspaces/ts-source-parser/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { TsSourceParser } from "./src/TsSourceParser.js";
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"name": "@nodesecure/ts-source-parser",
3+
"version": "1.0.0",
4+
"description": "TypeScript parser for AST XRay analysis",
5+
"type": "module",
6+
"exports": "./index.js",
7+
"types": "./index.d.ts",
8+
"scripts": {
9+
"lint": "eslint src test",
10+
"prepublishOnly": "pkg-ok",
11+
"test-only": "glob -c \"node --test-reporter=spec --test\" \"./test/**/*.spec.js\"",
12+
"test": "c8 --all --src ./src -r html npm run test-only",
13+
"check": "cross-env npm run lint && npm run test"
14+
},
15+
"repository": {
16+
"type": "git",
17+
"url": "git+https://github.com/NodeSecure/js-x-ray.git"
18+
},
19+
"keywords": [
20+
"typescript",
21+
"estree",
22+
"ast",
23+
"utils"
24+
],
25+
"files": [
26+
"src"
27+
],
28+
"author": "Michelet Jean <[email protected]>",
29+
"license": "MIT",
30+
"bugs": {
31+
"url": "https://github.com/NodeSecure/js-x-ray/issues"
32+
},
33+
"homepage": "https://github.com/NodeSecure/js-x-ray/tree/master/workspaces/ts-source-parser#readme",
34+
"dependencies": {
35+
"@typescript-eslint/typescript-estree": "^6.19.1"
36+
}
37+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Import Third-party Dependencies
2+
import { parse } from "@typescript-eslint/typescript-estree";
3+
4+
// CONSTANTS
5+
export const tsParsingOptions = {
6+
jsDocParsingMode: "none",
7+
jsx: true,
8+
loc: true,
9+
range: false
10+
};
11+
12+
export class TsSourceParser {
13+
/**
14+
* @param {object} options
15+
*/
16+
parse(source, options = {}) {
17+
const { body } = parse(source, {
18+
...tsParsingOptions,
19+
...options
20+
});
21+
22+
return body;
23+
}
24+
}
25+
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Import Node.js Dependencies
2+
import { describe, it } from "node:test";
3+
import assert from "assert/strict";
4+
5+
// Import Internal Dependencies
6+
import { TsSourceParser } from "../src/TsSourceParser.js";
7+
8+
describe("TsSourceParser", () => {
9+
describe("parse", () => {
10+
const parser = new TsSourceParser();
11+
12+
it("should correctly parse with default options", () => {
13+
const source = "const x: number = 5;";
14+
const body = parser.parse(source);
15+
16+
assert.strictEqual(body[0].type, "VariableDeclaration");
17+
assert.ok(body[0].loc);
18+
assert.ok(body[0].range === undefined);
19+
});
20+
21+
it("should correctly parse with custom options", () => {
22+
const source = "const x: number = 5;";
23+
const body = parser.parse(source, { loc: false, range: true });
24+
25+
assert.strictEqual(body[0].type, "VariableDeclaration");
26+
assert.ok(body[0].loc === undefined);
27+
assert.ok(body[0].range);
28+
});
29+
30+
it("should not crash parsing JSX by default", () => {
31+
const source = `const Dropzone = forwardRef(({ children, ...params }, ref) => {
32+
const { open, ...props } = useDropzone(params);
33+
useImperativeHandle(ref, () => ({ open }), [open]);
34+
return <Fragment>{children({ ...props, open })}</Fragment>;
35+
});`;
36+
37+
assert.doesNotThrow(() => {
38+
parser.parse(source);
39+
});
40+
});
41+
42+
it("should crash parsing JSX if jsx: false", () => {
43+
const source = `const Dropzone = forwardRef(({ children, ...params }, ref) => {
44+
const { open, ...props } = useDropzone(params);
45+
useImperativeHandle(ref, () => ({ open }), [open]);
46+
return <Fragment>{children({ ...props, open })}</Fragment>;
47+
});`;
48+
49+
assert.throws(() => {
50+
parser.parse(source, { jsx: false });
51+
});
52+
});
53+
});
54+
});

0 commit comments

Comments
 (0)