Repository: dsherret/ts-nameof
Branch: main
Commit: 5e817b83998d
Files: 129
Total size: 163.2 KB
Directory structure:
gitextract_ndkir86v/
├── .gitignore
├── .travis.yml
├── .vscode/
│ └── launch.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DEVELOPMENT.md
├── LICENSE
├── README.md
├── dprint.json
├── lib/
│ ├── global.d.ts
│ └── global.tests.ts
├── package.json
├── packages/
│ ├── babel-plugin-ts-nameof/
│ │ ├── .npmignore
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ └── index.ts
│ │ └── tsconfig.json
│ ├── common/
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── errors.ts
│ │ │ └── index.ts
│ │ └── tsconfig.json
│ ├── scripts-common/
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── ArgsChecker.ts
│ │ │ └── index.ts
│ │ └── tsconfig.json
│ ├── tests-common/
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── runCommonTests.ts
│ │ └── tsconfig.json
│ ├── transforms-babel/
│ │ ├── .mocharc.yml
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── VisitSourceFileContext.ts
│ │ │ ├── helpers.ts
│ │ │ ├── index.ts
│ │ │ ├── parse.ts
│ │ │ ├── tests/
│ │ │ │ └── pluginTests.ts
│ │ │ └── transform.ts
│ │ └── tsconfig.json
│ ├── transforms-common/
│ │ ├── .mocharc.yml
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── StringOrTemplateExpressionBuilder.ts
│ │ │ ├── index.ts
│ │ │ ├── nodeFactories.ts
│ │ │ ├── nodeHelpers.ts
│ │ │ ├── nodes.ts
│ │ │ ├── printers.ts
│ │ │ ├── tests/
│ │ │ │ └── printerTests.ts
│ │ │ └── transformCallExpression.ts
│ │ └── tsconfig.json
│ ├── transforms-ts/
│ │ ├── .mocharc.yml
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── VisitSourceFileContext.ts
│ │ │ ├── helpers.ts
│ │ │ ├── index.ts
│ │ │ ├── parse.ts
│ │ │ ├── tests/
│ │ │ │ └── transformerFactoryTests.ts
│ │ │ ├── transform.ts
│ │ │ └── transformerFactory.ts
│ │ └── tsconfig.json
│ ├── ts-nameof/
│ │ ├── .mocharc.yml
│ │ ├── .npmignore
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── lib/
│ │ │ └── declarationFileTests.ts
│ │ ├── package.json
│ │ ├── scripts/
│ │ │ ├── common/
│ │ │ │ ├── createProject.ts
│ │ │ │ └── index.ts
│ │ │ ├── generation/
│ │ │ │ ├── createDeclarationFile.ts
│ │ │ │ └── main.ts
│ │ │ ├── tsconfig.json
│ │ │ └── verification/
│ │ │ ├── main.ts
│ │ │ └── verifyDeclarationFile.ts
│ │ ├── setup/
│ │ │ ├── custom.md
│ │ │ ├── fusebox.md
│ │ │ ├── gulp.md
│ │ │ ├── jest.md
│ │ │ ├── tsc.md
│ │ │ └── webpack.md
│ │ ├── src/
│ │ │ ├── main.ts
│ │ │ ├── tests/
│ │ │ │ ├── testFiles/
│ │ │ │ │ ├── GeneralTestFile.txt
│ │ │ │ │ ├── StreamNoNameofTestFile.txt
│ │ │ │ │ ├── StreamTestFile.txt
│ │ │ │ │ ├── globFolder/
│ │ │ │ │ │ └── MyGlobTestFile.txt
│ │ │ │ │ └── issues/
│ │ │ │ │ ├── 11-expected.txt
│ │ │ │ │ ├── 11-source.txt
│ │ │ │ │ ├── 8-expected.txt
│ │ │ │ │ └── 8-source.txt
│ │ │ │ └── text/
│ │ │ │ ├── helpers/
│ │ │ │ │ ├── fileHelpers.ts
│ │ │ │ │ ├── getTestFilePath.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── issuesTests.ts
│ │ │ │ ├── replaceInFilesTests.ts
│ │ │ │ └── replaceInTextTests.ts
│ │ │ └── text/
│ │ │ ├── getFileNamesFromGlobs.ts
│ │ │ ├── index.ts
│ │ │ ├── replaceInFiles.ts
│ │ │ └── replaceInText.ts
│ │ ├── ts-nameof.d.ts
│ │ └── tsconfig.json
│ └── ts-nameof.macro/
│ ├── .mocharc.yml
│ ├── .npmignore
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── scripts/
│ │ ├── common/
│ │ │ ├── createProject.ts
│ │ │ └── index.ts
│ │ ├── generation/
│ │ │ ├── createDeclarationFile.ts
│ │ │ └── main.ts
│ │ └── tsconfig.json
│ ├── src/
│ │ ├── index.js
│ │ ├── references.d.ts
│ │ └── tests/
│ │ ├── macroTests.ts
│ │ └── ts-nameof.macro/
│ │ └── index.js
│ ├── ts-nameof.macro.d.ts
│ └── tsconfig.json
└── tsconfig.common.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
/node_modules
/.vs
/bin
/dist
/obj
/temp
/temp
*.js.map
*.suo
*.csproj
*.csproj.user
*.sln
/packages/ts-nameof/temp/
/packages/*/dist/
/packages/*/node_modules/
*.tsbuildinfo
# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next
# OS X temporary files
.DS_Store
================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
- '16'
script:
- set -e
- yarn install
- yarn build
- yarn verify
================================================
FILE: .vscode/launch.json
================================================
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Run transforms-babel tests",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "--prefix", "packages/transforms-babel", "test:debug"],
"port": 9229,
"stopOnEntry": false
},
{
"type": "node",
"request": "launch",
"name": "Run transforms-common tests",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "--prefix", "packages/transforms-common", "test:debug"],
"port": 9229,
"stopOnEntry": false
},
{
"type": "node",
"request": "launch",
"name": "Run transforms-ts tests",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "--prefix", "packages/transforms-ts", "test:debug"],
"port": 9229,
"stopOnEntry": false
},
{
"type": "node",
"request": "launch",
"name": "Run ts-nameof.macro tests",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "--prefix", "packages/ts-nameof.macro", "test:debug"],
"port": 9229,
"stopOnEntry": false
},
{
"type": "node",
"request": "launch",
"name": "Run ts-nameof tests",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "--prefix", "packages/ts-nameof", "test:debug"],
"port": 9229,
"stopOnEntry": false
}
]
}
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or
advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team on twitter via direct message at
https://twitter.com/DavidSherret (DMs open). All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: CONTRIBUTING.md
================================================
# Logging Bugs
1. Start logging an issue in the [issue tracker](https://github.com/dsherret/ts-nameof/issues).
2. Clearly identify the problem and submit some reproduction code.
- Prune the reproduction to remove needless details.
3. State the current and expected behaviour.
4. State the version of ts-nameof (always show a reproduction of the bug on the latest version).
# Contributing Bug Fixes
1. Follow the instructions above about logging a bug. In addition:
1. State that you are going to work on the bug.
2. Discuss major structural changes in the issue before doing the work to ensure it makes sense and work isn't wasted.
2. Start working on the fix in a branch and submit a PR when done.
3. Ensure `yarn verify` passes when run in the root directory.
# Contributing Features
1. Log an issue in the [issue tracker](https://github.com/dsherret/ts-nameof/issues). In the issue:
1. Propose the change.
- Outline all changes that will be made to the public API.
- Discuss any structural changes to the code if necessary.
2. Wait for discussion and green light from [@dsherret](https://github.com/dsherret) (who will try to reply as soon as possible, but it might take a few days).
- Note: If the change is small and you think it wouldn't take you too much time, then feel free to start working on it and even submit a PR. Just beware that you're taking the risk that it could be denied.
2. After approval, start working on the change in a branch and submit a PR.
3. Read [DEVELOPMENT.md](DEVELOPMENT.md) for some useful information.
4. Ensure `yarn verify` passes when run in the root directory.
================================================
FILE: DEVELOPMENT.md
================================================
# Development
## Building
Open the root directory of the repo and run:
```bash
# install dependencies
yarn install
# build
yarn build
```
## Packages
- [packages/babel-plugin-ts-nameof](packages/babel-plugin-ts-nameof) - Transform plugin for Babel.
- [packages/common](packages/common) - Common code used by almost everything.
- [packages/scripts-common](packages/scripts-common) - Common scripts used by other packages.
- [packages/tests-common](packages/tests-common) - Tests used by some packages. Write all your transform tests here.
- [packages/transforms-babel](packages/transforms-babel) - Transforms from the Babel AST to the Common AST.
- [packages/transforms-common](packages/transforms-common) - Nameof transforms done in the Common AST.
- [packages/transforms-ts](packages/transforms-ts) - Transforms from the TypeScript AST to the Common AST.
- [packages/ts-nameof](packages/ts-nameof) - ts-nameof library for the TypeScript compiler.
- [packages/ts-nameof.macro](packages/ts-nameof) - ts-nameof.macro library for Babel macros.
## Standard Commands
```bash
# build (run in root dir)
yarn build
# run tests (run in root dir)
yarn test
# format the code (download dprint from dprint.dev)
dprint fmt
```
### Clean Rebuild
```
yarn clean && yarn build
```
## Declaration File
### Global Definitions
The global definitions are stored in [lib/global.d.ts](lib/global.d.ts). To make changes:
1. Add a failing test in [lib/global.tests.ts](lib/global.tests.ts) (failing test means you get a compile error)
2. Update [lib/global.d.ts](lib/global.d.ts).
3. Run `yarn create-declaration-file` in the root directory
### ts-nameof - Updating API
1. Update [packages/ts-nameof/lib/declarationFileTests.ts](packages/ts-nameof/lib/declarationFileTests.ts) with a failing test.
2. Update the API in [packages/ts-nameof/src/main.ts](packages/ts-nameof/src/main.ts).
3. Run `yarn create-declaration-file` in the root directory
## After Development
Run the following command in the root directory, which will check that everything is good:
```bash
yarn verify
```
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2019 David Sherret
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# ts-nameof
[](https://travis-ci.org/dsherret/ts-nameof)
[`nameof`](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/nameof) in TypeScript.
Monorepo for ts-nameof projects:
- [ts-nameof](packages/ts-nameof) (TypeScript compiler)
- [babel-plugin-ts-nameof](packages/babel-plugin-ts-nameof) (Babel compiler)
- [ts-nameof.macro](packages/ts-nameof.macro) (Babel compiler)
## Recommend: Don't use this package
See [here](https://github.com/dsherret/ts-nameof/issues/121).
## Setup
ts-nameof is a _compile time transform_ so it requires some setup. For setup instructions, see the packages above for the compiler you use.
## nameof transform
### `nameof(...)`
```ts
nameof(console);
nameof(console.log);
nameof(console["warn"]);
```
Transforms to:
```ts
"console";
"log";
"warn";
```
### `nameof<T>()`
```ts
nameof<MyInterface>();
nameof<Array<MyInterface>>();
nameof<MyNamespace.MyInnerInterface>();
```
Transforms to:
```ts
"MyInterface";
"Array";
"MyInnerInterface";
```
This is useful when working in the type domain.
### `nameof<T>(o => ...)`
```ts
nameof<MyInterface>(o => o.prop);
```
Transforms to:
```ts
"prop";
```
## nameof.full transform
### `nameof.full(...)`
```ts
nameof.full(console.log);
nameof.full(window.alert.length, 1);
nameof.full(window.alert.length, 2);
nameof.full(window.alert.length, -1);
nameof.full(window.alert.length, -2);
nameof.full(window.alert.length, -3);
```
Transforms to:
```ts
"console.log";
"alert.length";
"length";
"length";
"alert.length";
"window.alert.length";
```
### `nameof.full<T>()`
```ts
nameof.full<MyNamespace.MyInnerInterface>();
nameof.full<MyNamespace.MyInnerInterface>(1);
nameof.full<Array<MyInterface>>();
```
Transforms to:
```ts
"MyNamespace.MyInnerInterface";
"MyInnerInterface";
"Array";
```
### `nameof.full<T>(o => ...)`
```ts
nameof.full<MyInterface>(o => o.prop.prop2);
nameof.full<MyInterface>(o => o.prop.prop2.prop3, 1);
```
Transforms to:
```ts
"prop.prop2";
"prop2.prop3";
```
### `nameof.interpolate(value)`
Writing the following:
```ts
nameof.full(myObj.prop[i]);
```
...does not interpolate the node in the computed property.
```ts
"myObj.prop[i]";
```
If you want to interpolate the value then you can specify that explicitly with a `nameof.interpolate` function.
```ts
nameof.full(myObj.prop[nameof.interpolate(i)]);
```
Transforms to:
```ts
`myObj.prop[${i}]`;
```
## nameof.toArray transform
Contributed by: [@cecilyth](https://github.com/cecilyth)
### `nameof.toArray(...)`
```ts
nameof.toArray(myObject, otherObject);
nameof.toArray(obj.firstProp, obj.secondProp, otherObject, nameof.full(obj.other));
```
Transforms to:
```ts
["myObject", "otherObject"];
["firstProp", "secondProp", "otherObject", "obj.other"];
```
### `nameof.toArray<T>(o => [...])`
```ts
nameof.toArray<MyType>(o => [o.firstProp, o.otherProp.secondProp, o.other]);
nameof.toArray<MyType>(o => [o.prop, nameof.full(o.myProp.otherProp, 1)]);
```
Transforms to:
```ts
["firstProp", "secondProp", "other"];
["prop", "myProp.otherProp"];
```
## nameof.split transform
Contributed by: [@cecilyth](https://github.com/cecilyth)
### `nameof.split(...)`
```ts
nameof.split(myObj.prop.prop2);
nameof.split(myObj.prop.prop2, 1);
nameof.split(myObj.prop.prop2, -1);
nameof.split(myObj.prop.prop2).join("/");
```
Transforms to:
```ts
["myObj", "prop", "prop2"];
["prop", "prop2"];
["prop2"];
["myObj", "prop", "prop2"].join("/"); // "myObj/prop/prop2"
```
### `nameof.split<T>(o => ...)`
```ts
nameof.split<MyInterface>(o => o.prop.prop2.prop3);
nameof.split<MyInterface>(o => o.prop.prop2.prop3, 1);
nameof.split<MyInterface>(o => o.prop.prop2.prop3, -1);
nameof.split<IState>(s => s.a.b.c).join("/");
```
Transforms to:
```ts
["prop", "prop2", "prop3"];
["prop2", "prop3"];
["prop3"];
["a", "b", "c"].join("/"); // "a/b/c"
```
## Other
- [Contributing](CONTRIBUTING.md)
- [Development](DEVELOPMENT.md)
================================================
FILE: dprint.json
================================================
{
"incremental": true,
"lineWidth": 160,
"indentWidth": 2,
"includes": ["**/*.{ts,tsx,js,jsx,json,md}"],
"excludes": [
"common",
"**/node_modules",
"**/*-lock.json"
],
"plugins": [
"https://plugins.dprint.dev/typescript-0.60.0.wasm",
"https://plugins.dprint.dev/json-0.7.0.wasm",
"https://plugins.dprint.dev/markdown-0.11.3.wasm"
]
}
================================================
FILE: lib/global.d.ts
================================================
/**
* Gets a string representation of the final identifier of the given expression.
*
* @example nameof<MyInterface>() -> "MyInterface"
* @example nameof<Array<MyInterface>>() -> "Array"
* @example nameof<MyNamespace.MyInnerInterface>() -> "MyInnerInterface"
* @example nameof<MyInterface>(o => o.prop) -> "prop"
*
* @param func An optional function for which the last identifier of the expression will be parsed.
*/
declare function nameof<T>(func?: (obj: T) => any): string;
/**
* Gets a string representation of the last identifier of the given expression.
*
* @example nameof(console) -> "console"
* @example nameof(console.log) -> "log"
* @example nameof(console["warn"]) -> "warn"
*
* @param obj An expression for which the last identifier will be parsed.
*/
declare function nameof(obj: any): string;
declare namespace nameof {
/**
* Gets the string representation of the entire type parameter expression.
*
* @example nameof.full<MyNamespace.MyInnerInterface>() -> "MyNamespace.MyInnerInterface"
* @example nameof.full<MyNamespace.MyInnerInterface>(1) -> "MyInnerInterface"
* @example nameof.full<Array<MyInterface>>() -> "Array"
* @example nameof.full<MyNamespace.AnotherNamespace.MyInnerInterface>>(-1) -> "MyInnerInterface"
*
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function full<T>(periodIndex?: number): string;
/**
* Gets the string representation of the entire resultant expression.
*
* @example nameof.full<MyInterface>(o => o.prop.prop2) -> "prop.prop2"
* @example nameof.full<MyInterface>(o => o.prop.prop2.prop3, 1) -> "prop2.prop3"
* @example nameof.full<MyInterface>(o => o.prop.prop2.prop3, -1) -> `"prop3"
*
* @param func A function for which the result will be parsed, excluding the parameter's identifier.
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function full<T>(func: (obj: T) => any, periodIndex?: number): string;
/**
* Gets the string representation of the entire given expression.
*
* @example nameof.full(console.log) -> "console.log"
* @example nameof.full(window.alert.length, -1) -> "length"
* @example nameof.full(window.alert.length, 2) -> "length"
*
* @param obj The expression which will be parsed.
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function full(obj: any, periodIndex?: number): string;
/**
* Gets an array containing the string representation of the final identifier of each expression in the array returned by the provided function.
*
* @example nameof.toArray<MyType>(o => [o.firstProp, o.otherProp.secondProp, o.other]) -> ["firstProp", "secondProp", "other"]
* @example nameof.toArray<MyType>(o => [o.prop, nameof.full(o.myProp.otherProp, 1)]) -> ["prop", "myProp.otherProp"]
*
* @param func A function returning an array of expressions to be parsed, excluding the parameter's identifier.
*/
function toArray<T>(func: (obj: T) => any[]): string[];
/**
* Gets an array containing the string representation of each expression in the arguments.
*
* @example nameof.toArray(myObject, otherObject) -> ["myObject", "otherObject"]
* @example nameof.toArray(obj.firstProp, obj.secondProp, otherObject, nameof.full(obj.other)) -> ["firstProp", "secondProp", "otherObject", "obj.other"]
*
* @param args An array of expressions to be parsed.
*/
function toArray(...args: any[]): string[];
/**
* Embeds an expression into the string representation of the result of nameof.full.
*
* @example nameof.full(myObj.prop[nameof.interpolate(i)]) -> `myObj.prop[${i}]`
*
* @param value The value to interpolate.
*/
function interpolate<T>(value: T): T;
/**
* Gets an array of strings where each element is a subsequent part of the expression provided.
*
* @example nameof.split<MyInterface>(o => o.prop.prop2.prop3) -> ["prop", "prop2", "prop3"]
* @example nameof.split<MyInterface>(o => o.prop.prop2.prop3, 1) -> ["prop2", "prop3"]
* @example nameof.split<MyInterface>(o => o.prop.prop2.prop3, -1) -> ["prop", "prop2"]
*
* @param func A function for which the resultant parts will be parsed, excluding the parameter's identifier.
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function split<T>(func: (obj: T) => any, periodIndex?: number): string[];
/**
* Gets an array of strings where each element is a subsequent part of the expression provided.
*
* @example nameof.split(myObj.prop.prop2.prop3) -> ["myObj", "prop", "prop2", "prop3"]
* @example nameof.split(myObj.prop.prop2.prop3, -3);`, `["prop", "prop2", "prop3"];
* @example nameof.split(myObj.prop.prop2.prop3, 2);`, `["prop2", "prop3"]
*
* @param obj An expression for which the parts will be parsed.
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function split(obj: any, periodIndex?: number): string[];
}
================================================
FILE: lib/global.tests.ts
================================================
/// <reference path="./global.d.ts" />
namespace TestNamespace {
export interface TestType {
prop: string;
}
}
class TestClass {
prop1 = "";
prop2 = "";
}
// nameof tests
nameof(TestClass); // $ExpectType string
nameof<TestNamespace.TestType>(); // $ExpectType string
nameof<TestClass>(t => t.prop1); // $ExpectType string
// nameof.full tests
const testInstance = new TestClass();
nameof.full(testInstance.prop1); // $ExpectType string
nameof.full(testInstance.prop1, 1); // $ExpectType string
nameof.full<TestNamespace.TestType>(); // $ExpectType string
nameof.full<TestNamespace.TestType>(1); // $ExpectType string
nameof.full<TestClass>(t => t.prop1); // $ExpectType string
nameof.full<TestClass>(t => t.prop1, 1); // $ExpectType string
// nameof.toArray tests
nameof.toArray(testInstance.prop1); // $ExpectType string[]
nameof.toArray(testInstance.prop1, testInstance.prop2); // $ExpectType string[]
nameof.toArray<TestClass>(t => [t.prop1]); // $ExpectType string[]
// nameof.split tests
nameof.split(testInstance.prop1); // $ExpectType string[]
nameof.split(testInstance.prop1, 1); // $ExpectType string[]
nameof.split<TestClass>(obj => obj.prop1); // $ExpectType string[]
nameof.split<TestClass>(obj => obj.prop1, 1); // $ExpectType string[]
// nameof.interpolate tests
nameof.interpolate(""); // $ExpectType ""
// reference type test
const myObj = { test: "" };
nameof(myObj); // $ExpectType string
nameof.full(myObj); // $ExpectType string
nameof.toArray(myObj); // $ExpectType string[]
// primitive type test
const myStr = "";
nameof(myStr); // $ExpectType string
nameof.full(myStr); // $ExpectType string
nameof.toArray(myStr); // $ExpectType string[]
// null test
const nullTypedVar = null;
nameof(nullTypedVar); // $ExpectType string
nameof.full(nullTypedVar); // $ExpectType string
nameof.toArray(nullTypedVar); // $ExpectType string[]
// undefined test
const undefinedTypedVar = undefined;
nameof(undefinedTypedVar); // $ExpectType string
nameof.full(undefinedTypedVar); // $ExpectType string
nameof.toArray(undefinedTypedVar); // $ExpectType string[]
================================================
FILE: package.json
================================================
{
"name": "ts-nameof-workspace",
"private": true,
"workspaces": [
"packages/common",
"packages/scripts-common",
"packages/tests-common",
"packages/transforms-common",
"packages/transforms-babel",
"packages/transforms-ts",
"packages/ts-nameof",
"packages/ts-nameof.macro",
"packages/babel-plugin-ts-nameof"
],
"scripts": {
"clean": "yarn workspaces run clean",
"build": "yarn workspaces run build",
"test": "yarn workspaces run test",
"verify": "yarn test && yarn verify-declaration-file",
"create-declaration-file": "yarn workspace ts-nameof build:declarations && yarn workspace ts-nameof.macro build:declarations",
"verify-declaration-file": "yarn workspace ts-nameof verify-declaration-file"
}
}
================================================
FILE: packages/babel-plugin-ts-nameof/.npmignore
================================================
/node_modules
/.vscode
/.vs
/.git
/obj
/temp
/bin
/src
/dist/tests
/setup
/scripts
/lib
*.js.map
*.v12.suo
*.csproj
*.csproj.user
*.sln
*.log
.travis.yml
.gitignore
CHANGELOG.md
.mocharc.yml
tsconfig.json
================================================
FILE: packages/babel-plugin-ts-nameof/LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2019 David Sherret
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: packages/babel-plugin-ts-nameof/README.md
================================================
# babel-plugin-ts-nameof
[](https://badge.fury.io/js/babel-plugin-ts-nameof)
[](https://travis-ci.org/dsherret/ts-nameof)
[](http://github.com/badges/stability-badges)
[`nameof`](https://msdn.microsoft.com/en-us/library/dn986596.aspx) in TypeScript.
## Setup
1. Run:
```
npm install babel-plugin-ts-nameof @types/ts-nameof --save-dev
```
2. Add an entry to `.babelrc` for `babel-plugin-ts-nameof`:
```
{
"plugins": ["babel-plugin-ts-nameof"]
}
```
## Transforms
[Read here](https://github.com/dsherret/ts-nameof/blob/master/README.md)
## Other
- [Contributing](https://github.com/dsherret/ts-nameof/blob/master/CONTRIBUTING.md)
- [Development](https://github.com/dsherret/ts-nameof/blob/master/DEVELOPMENT.md)
================================================
FILE: packages/babel-plugin-ts-nameof/package.json
================================================
{
"name": "babel-plugin-ts-nameof",
"version": "4.2.1",
"description": "nameof in TypeScript for babel.",
"main": "dist/index.js",
"scripts": {
"clean": "rimraf dist && tsc --b --clean",
"build": "tsc --b",
"test": "echo 'ignore'",
"dopublish": "npm run install && npm run build && echo \"Run: npm publish --otp\""
},
"keywords": [
"nameof",
"typescript",
"transforms",
"babel"
],
"repository": {
"type": "git",
"url": "git+https://github.com/dsherret/ts-nameof.git",
"directory": "packages/babel-plugin-ts-nameof"
},
"author": "David Sherret",
"license": "MIT",
"dependencies": {
"@ts-nameof/transforms-babel": "^4.2.1"
},
"devDependencies": {
"@babel/core": "^7.16.5",
"@babel/preset-typescript": "^7.16.5",
"@ts-nameof/tests-common": "^4.2.0",
"@types/babel__core": "^7.1.17",
"@types/babel__generator": "^7.6.3",
"@types/babel__template": "^7.4.1",
"@types/babel__traverse": "^7.14.2",
"@types/node": "^17.0.0",
"mocha": "^9.1.3",
"rimraf": "^3.0.2",
"ts-node": "^10.4.0",
"typescript": "^4.5.4"
}
}
================================================
FILE: packages/babel-plugin-ts-nameof/src/index.ts
================================================
export { plugin as default } from "@ts-nameof/transforms-babel";
================================================
FILE: packages/babel-plugin-ts-nameof/tsconfig.json
================================================
{
"compilerOptions": {
"outDir": "./dist"
},
"extends": "../../tsconfig.common.json",
"include": ["./src"],
"references": [
{ "path": "../transforms-babel" },
{ "path": "../tests-common" }
]
}
================================================
FILE: packages/common/.npmignore
================================================
tsconfig.json
tsconfig.tsbuildinfo
*.log
*.js.map
src
================================================
FILE: packages/common/README.md
================================================
# ts-nameof - Common
Contains common utilities functions.
================================================
FILE: packages/common/package.json
================================================
{
"name": "@ts-nameof/common",
"version": "4.2.1",
"description": "ts-nameof - Common code across packages.",
"main": "./dist/index.js",
"author": "David Sherret",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"scripts": {
"clean": "rimraf dist && tsc --b --clean",
"build": "tsc --b",
"test": "echo 'ignore'"
},
"dependencies": {
},
"devDependencies": {
"typescript": "^4.5.4",
"rimraf": "^3.0.2"
}
}
================================================
FILE: packages/common/src/errors.ts
================================================
export function throwError(message: string): never {
throw new Error(`[ts-nameof]: ${sanitizeMessage(message)}`);
}
export function throwErrorForSourceFile(message: string, sourceFilePath: string): never {
throw new Error(`[ts-nameof:${sourceFilePath}]: ${sanitizeMessage(message)}`);
}
export function assertNever(value: never, message: string): never {
return throwError(message);
}
function sanitizeMessage(message: string) {
return message.replace(/^\[ts\-nameof[^\]]*\]: /, "");
}
================================================
FILE: packages/common/src/index.ts
================================================
export * from "./errors";
================================================
FILE: packages/common/tsconfig.json
================================================
{
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "./dist"
},
"extends": "../../tsconfig.common.json",
"include": ["./src/**/*.ts"]
}
================================================
FILE: packages/scripts-common/.npmignore
================================================
tsconfig.json
tsconfig.tsbuildinfo
src
*.log
*.js.map
================================================
FILE: packages/scripts-common/README.md
================================================
# ts-nameof - Scripts Common
Contains scripts for use across packages.
================================================
FILE: packages/scripts-common/package.json
================================================
{
"name": "@ts-nameof/scripts-common",
"version": "4.0.2",
"description": "ts-nameof - Common scripts for ts-nameof packages.",
"main": "./dist/index.js",
"author": "David Sherret",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"scripts": {
"clean": "rimraf dist && tsc --b --clean",
"build": "tsc --b",
"test": "echo 'ignore'"
},
"devDependencies": {
"@types/mocha": "^9.0.0",
"@types/node": "^17.0.0",
"rimraf": "^3.0.2",
"typescript": "^4.5.4"
}
}
================================================
FILE: packages/scripts-common/src/ArgsChecker.ts
================================================
export class ArgsChecker {
private readonly originalArgs: ReadonlyArray<string>;
private readonly args: string[];
constructor(args?: string[]) {
this.args = args || process.argv.slice(2);
this.originalArgs = [...this.args];
}
checkHasArg(argName: string) {
if (this.originalArgs.length === 0) {
return true; // run all
}
return this.checkHasExplicitArg(argName);
}
checkHasExplicitArg(argName: string) {
const index = this.args.indexOf(argName);
if (index === -1) {
return false;
}
this.args.splice(index, 1);
return true;
}
verifyArgsUsed() {
if (this.args.length > 0) {
console.error(`Unknown args: ${this.args.join(", ")}`);
}
}
}
================================================
FILE: packages/scripts-common/src/index.ts
================================================
export * from "./ArgsChecker";
================================================
FILE: packages/scripts-common/tsconfig.json
================================================
{
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "./dist"
},
"extends": "../../tsconfig.common.json",
"include": ["./src/**/*.ts"]
}
================================================
FILE: packages/tests-common/.npmignore
================================================
tsconfig.json
tsconfig.tsbuildinfo
src
*.log
*.js.map
================================================
FILE: packages/tests-common/README.md
================================================
# ts-nameof - Tests Common
Contains tests that should work across any implementation of ts-nameof (whether babel or the typescript compiler).
================================================
FILE: packages/tests-common/package.json
================================================
{
"name": "@ts-nameof/tests-common",
"version": "4.2.0",
"description": "Common tests for ts-nameof packages.",
"main": "./dist/index.js",
"author": "David Sherret",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"scripts": {
"clean": "rimraf dist && tsc --b --clean",
"build": "tsc --b",
"test": "echo 'ignore'"
},
"devDependencies": {
"@dprint/formatter": "^0.1.5",
"@dprint/typescript": "^0.60.0",
"@types/mocha": "^9.0.0",
"@types/node": "^17.0.0",
"rimraf": "^3.0.2",
"typescript": "^4.5.4"
}
}
================================================
FILE: packages/tests-common/src/index.ts
================================================
export * from "./runCommonTests";
================================================
FILE: packages/tests-common/src/runCommonTests.ts
================================================
import { createFromBuffer } from "@dprint/formatter";
// @ts-ignore
import { getBuffer } from "@dprint/typescript";
import * as assert from "assert";
import * as path from "path";
const formatter = createFromBuffer(getBuffer());
formatter.setConfig({ indentWidth: 2 }, {});
/**
* Runs tests across different compilers.
* @param getTransformedText Function to get the transformed text.
* @param options Options for running the tests.
*/
export function runCommonTests(getTransformedText: (text: string) => string, options: { commonPrefix?: string } = {}) {
describe("nameof", () => {
describe("bad call expressions", () => {
it("should throw if someone does not provide arguments or type arguments", () => {
runThrowTest("nameof();", "Call expression must have one argument or type argument: nameof()");
});
});
describe("argument", () => {
it("should get the result of an identifier", () => {
runTest(`nameof(myObj);`, `"myObj";`);
});
it("should get the result of the this keyword", () => {
runTest(`nameof(this);`, `"this";`);
});
it("should get the result of a property access expression", () => {
runTest(`nameof(myObj.prop);`, `"prop";`);
});
it("should get the result of an expression with a parenthesized expression", () => {
runTest(`nameof((myObj).prop);`, `"prop";`);
});
it("should get the result of an expression with a type assertion", () => {
runTest(`nameof((myObj as any).prop);`, `"prop";`);
});
it("should get the result of a property access expression with null assertion operators", () => {
runTest(`nameof(myObj!.prop!);`, `"prop";`);
});
it("should get the result of an identifier with a dollar sign", () => {
runTest(`nameof(myObj.$prop);`, `"$prop";`);
});
it("should resolve to string when nesting nameofs", () => {
runTest(`nameof(nameof(testing));`, `"testing";`);
});
});
describe("type parameter", () => {
it("should get the result of an identifier", () => {
runTest(`nameof<Test>();`, `"Test";`);
});
it("should get the result of a fully qualified name", () => {
runTest(`nameof<This.Is.A.Test>();`, `"Test";`);
});
it("should get an identifier with a dollar sign", () => {
runTest(`nameof<Test$>();`, `"Test$";`);
});
it("should handle when someone uses an import type as not the last node", () => {
runTest(`nameof<import('test').prop>();`, `"prop";`);
});
it("should throw when someone only uses an import type", () => {
runThrowTest(`nameof<import('test')>();`, getNotSupportedErrorText("import(\"test\")"));
});
it("should throw when someone only uses an import type with typeof", () => {
runThrowTest(`nameof<typeof import('test')>();`, getNotSupportedErrorText("typeof import(\"test\")"));
});
});
describe("computed properties", () => {
it("should not allow a computed property to be at the end with a number", () => {
runThrowTest(`nameof(anyProp[0]);`, getFirstAccessedPropertyMustNotBeComputedErrorText(`anyProp[0]`));
});
it("should get after the period", () => {
runTest(`nameof(anyProp[0].prop);`, `"prop";`);
});
it("should get the string inside the computed property", () => {
runTest(`nameof(obj["prop"]);`, `"prop";`);
});
it("should get the string inside the computed property for a function", () => {
runTest(`nameof<MyInterface>(i => i["prop"]);`, `"prop";`);
});
it("should not allow a computed property to be at the end with a number when using a function", () => {
runThrowTest(`nameof<MyInterface>(i => i.prop[0]);`, getFirstAccessedPropertyMustNotBeComputedErrorText("(i) => i.prop[0]"));
});
it("should not allow an identifier nested in a computed property", () => {
runThrowTest(`nameof<MyInterface>(i => i.prop[prop[0]]);`, getFirstAccessedPropertyMustNotBeComputedErrorText("(i) => i.prop[prop[0]]"));
});
});
describe("array", () => {
it("should not allow only an array", () => {
runThrowTest(`nameof([0]);`, getNotSupportedErrorText("[0]"));
});
it("should allow getting an array's property", () => {
runTest(`nameof([].length);`, `"length";`);
});
});
describe("with function", () => {
it("should get the last string", () => {
runTest(`nameof<MyInterface>(i => i.prop1.prop2);`, `"prop2";`);
});
it("should get from the return statement", () => {
// no reason for people to do this, but don't bother complaining
runTest(`nameof<MyInterface>(i => { console.log('test'); return i.prop1.prop2; });`, `"prop2";`);
});
it("should handle when someone uses an import type", () => {
runTest(`nameof<import('test')>(x => x.Foo);`, `"Foo";`);
});
it("should get when using an element access expression directly on the object", () => {
runTest(`nameof<MyInterface>(i => i["prop1"]);`, `"prop1";`);
});
it("should throw when using an element access expression directly on the object and it is not a string", () => {
runThrowTest(`nameof<MyInterface>(i => i[0]);`, getFirstAccessedPropertyMustNotBeComputedErrorText(`(i) => i[0]`));
});
it("should throw when the function doesn't have a period", () => {
runThrowTest(`nameof<MyInterface>(i => i);`, "A property must be accessed on the object: (i) => i");
});
it("should throw when the function doesn't have a return statement", () => {
const errorPrefix = "Cound not find return statement with an expression in function expression: ";
const possibleMessages = [
errorPrefix + "{ i; }", // babel
errorPrefix + "{\n i;\n}", // typescript
];
runThrowTest(`nameof<MyInterface>(i => { i; });`, possibleMessages);
});
});
describe("literals", () => {
it("should leave the string literal as-is", () => {
// this allows for nested nameofs
runTest(`nameof("test");`, `"test";`);
});
it("should transform a numeric literal as a string", () => {
runTest(`nameof(5);`, `"5";`);
});
});
describe("interpolate", () => {
it("should throw when providing nameof.interpolate to nameof", () => {
runThrowTest(`nameof(nameof.interpolate(5));`, [
getNotSupportedErrorText("nameof.interpolate(5)"),
// it will be this for babel because it checks the parent nodes
getUnusedNameofInterpolateErrorText("5"),
]);
});
});
describe("template expression", () => {
it("should return a no substitution template literal", () => {
runTest("nameof(`testing`);", "`testing`;");
});
it("should return the template expression when it has only a template tail", () => {
runTest("nameof(`testing${test}final`);", "`testing${test}final`;");
});
it("should return the template expression when it has a template middle", () => {
runTest("nameof(`testing${other}asdf${test}${asdf}final`);", "`testing${other}asdf${test}${asdf}final`;");
});
it("should return the template expression when it starts and ends with one", () => {
runTest("nameof(`${other}`);", "`${other}`;");
});
it("should return the template expression when it starts and ends with multiple", () => {
runTest("nameof(`${other}${asdf}${test}`);", "`${other}${asdf}${test}`;");
});
it("should throw when a nameof.interpolate is not used", () => {
runThrowTest("nameof(`${nameof.interpolate(other)}`);", getUnusedNameofInterpolateErrorText("other"));
});
});
describe("other", () => {
it("should ignore spread syntax", () => {
runTest(`nameof(...test);`, `"test";`);
});
});
});
describe("nameof.full", () => {
describe("bad call expressions", () => {
it("should throw if someone does not provide arguments or type arguments", () => {
runThrowTest("nameof.full();", "Unsupported use of nameof.full: nameof.full()");
});
});
describe("argument", () => {
it("should include everything when no count arg is provided", () => {
runTest(`nameof.full(obj.prop.other);`, `"obj.prop.other";`);
});
it("should not include null assertion operators", () => {
runTest(`nameof.full(obj!.prop!.other!);`, `"obj.prop.other";`);
});
it("should not include null assertion operators when also using element access expressions", () => {
runTest(`nameof.full(obj!.prop![0].other!);`, `"obj.prop[0].other";`);
});
it("should escape string literals in element access expressions", () => {
runTest(`nameof.full(obj.prop["other"]);`, `"obj.prop[\\"other\\"]";`);
});
it("should allow using a period index", () => {
runTest("nameof.full(MyTest.Test.This, 1);", `"Test.This";`);
});
it("should allow using a period index of 0", () => {
runTest("nameof.full(MyTest.Test.This, 0);", `"MyTest.Test.This";`);
});
it("should allow using a period index up to its max value", () => {
runTest("nameof.full(MyTest.Test.This, 2);", `"This";`);
});
it("should allow using a negative period index", () => {
runTest("nameof.full(MyTest.Test.This, -1);", `"This";`);
});
it("should allow using a negative period index to its max value", () => {
runTest("nameof.full(MyTest.Test.This, -3);", `"MyTest.Test.This";`);
});
it("should throw when the periodIndex is not a number literal", () => {
runThrowTest("nameof.full(MyTest.Test, 'test')", `Expected count to be a number, but was: "test"`);
});
it("should throw when the periodIndex is greater than the number of periods", () => {
runThrowTest("nameof.full(MyTest.Test, 2)", "Count of 2 was larger than max count of 1: nameof.full(MyTest.Test, 2)");
});
it("should throw when the absolute value of the negative periodIndex is greater than the number of periods + 1", () => {
runThrowTest("nameof.full(MyTest.Test, -3)", "Count of -3 was larger than max count of -2: nameof.full(MyTest.Test, -3)");
});
it("should resolve to string when nesting nameofs", () => {
runTest(`nameof.full(nameof(testing));`, `"testing";`);
});
it("should get the result of the super keyword", () => {
runTest(
`class Test {\n constructor() {\n nameof.full(super.test);\n }\n}`,
`class Test {\n constructor() {\n "super.test";\n }\n}`,
);
});
});
describe("type parameter", () => {
it("should include everything when no count arg is provided", () => {
runTest(`nameof.full<Some.Test.Name>();`, `"Some.Test.Name";`);
});
it("should allow using a period index", () => {
runTest("nameof.full<MyTest.Test.This>(1);", `"Test.This";`);
});
it("should allow using a period index of 0", () => {
runTest("nameof.full<MyTest.Test.This>(0);", `"MyTest.Test.This";`);
});
it("should allow using a period index up to its max value", () => {
runTest("nameof.full<MyTest.Test.This>(2);", `"This";`);
});
it("should allow using a negative period index", () => {
runTest("nameof.full<MyTest.Test.This>(-1);", `"This";`);
});
it("should allow using a negative period index to its max value", () => {
runTest("nameof.full<MyTest.Test.This>(-3);", `"MyTest.Test.This";`);
});
it("should throw when the periodIndex is not a number literal", () => {
runThrowTest("nameof.full<MyTest.Test>('test')", `Expected count to be a number, but was: "test"`);
});
it("should throw when the periodIndex is greater than the number of periods", () => {
runThrowTest("nameof.full<MyTest.Test>(2)", "Count of 2 was larger than max count of 1: nameof.full<MyTest.Test>(2)");
});
it("should throw when the absolute value of the negative periodIndex is greater than the number of periods + 1", () => {
runThrowTest("nameof.full<MyTest.Test>(-3)", "Count of -3 was larger than max count of -2: nameof.full<MyTest.Test>(-3)");
});
it("should throw when someone uses an import type", () => {
runThrowTest(`nameof.full<import('test').other.test>();`, getNotSupportedErrorText("import(\"test\").other.test"));
});
});
describe("arrays", () => {
it("should include the brackets", () => {
runTest(`nameof.full(anyProp[0].myProp);`, `"anyProp[0].myProp";`);
});
});
describe("with function", () => {
it("should get the text", () => {
runTest(`nameof.full<MyInterface>(i => i.prop1.prop2);`, `"prop1.prop2";`);
});
it("should get the text without the null assertion operator", () => {
runTest(`nameof.full<MyInterface>(i => i.prop1!.prop2!);`, `"prop1.prop2";`);
});
it("should get the text when there's a trailing comma with whitespace", () => {
runTest("nameof.full<IState>(state => state.field.dates, );", `"field.dates";`);
});
it("should get the text when using a function", () => {
runTest(`nameof.full<MyInterface>(function(i) { return i.prop1.prop2; });`, `"prop1.prop2";`);
});
it("should get the text when providing a period", () => {
runTest(`nameof.full<MyInterface>(i => i.prop1.prop2, 0);`, `"prop1.prop2";`);
runTest(`nameof.full<MyInterface>(i => i.prop1.prop2, 1);`, `"prop2";`);
runTest(`nameof.full<MyInterface>(i => i.prop1.prop2.prop3, -1);`, `"prop3";`);
});
it("should throw when the function doesn't have a period", () => {
runThrowTest(`nameof.full<MyInterface>(i => i);`, "A property must be accessed on the object: (i) => i");
});
it("should throw when someone nests a function within a function", () => {
runThrowTest(`nameof.full<MyInterface>(i => () => 5);`, "A property must be accessed on the object: (i) => () => 5");
});
});
describe("interpolate", () => {
const singleArgumentErrorMessage = "Unexpected scenario where a nameof.interpolate function did not have a single argument.";
it("should interpolate the provided expression", () => {
runTest(`nameof.full(Test.Other[nameof.interpolate(other)]);`, "`Test.Other[${other}]`;");
});
it("should interpolate when using a function", () => {
runTest(`nameof.full<a>(a => a.b.c[nameof.interpolate(index)].d);`, "`b.c[${index}].d`;");
});
it("should throw when the interpolate function has zero arguments", () => {
runThrowTest(`nameof.full(Test.Other[nameof.interpolate()]);`, singleArgumentErrorMessage);
});
it("should throw when the interpolate function has multiple arguments", () => {
runThrowTest(`nameof.full(Test.Other[nameof.interpolate(test, test)]);`, singleArgumentErrorMessage);
});
it("should throw when a nameof.interpolate is not used inside a nameof.full", () => {
runThrowTest("nameof.interpolate(some.expression);", getUnusedNameofInterpolateErrorText("some.expression"));
});
it("should handle the scenarios in issue #104", () => {
runTest("nameof.full(m.Data[nameof.interpolate(i)].Title);", "`m.Data[${i}].Title`;");
runTest("nameof.full(m.Data[i].Title);", `"m.Data[i].Title";`);
});
});
});
describe("toArray", () => {
it("should return an array of values when given a function that returns an array as input", () => {
runTest(`nameof.toArray<MyInterface>(o => [o.Prop1, o.Prop2, o.Prop3]);`, `["Prop1", "Prop2", "Prop3"];`);
});
it("should return an array of values when given multiple arguments", () => {
runTest(`nameof.toArray(myObject.Prop1, otherObject.Prop2);`, `["Prop1", "Prop2"];`);
});
it("should return an array with a single element if a non-function argument is passed", () => {
runTest(`nameof.toArray(myObject.Prop1);`, `["Prop1"];`);
});
it("should support nested nameof calls", () => {
runTest(`nameof.toArray(nameof.full(Some.Qualified.Name), Some.Qualified.Name);`, `["Some.Qualified.Name", "Name"];`);
});
it("should support a non-arrow function expression", () => {
runTest(`nameof.toArray<MyInterface>(function(o) { return [o.Prop1, o.Prop2]; });`, `["Prop1", "Prop2"];`);
});
it("should throw when the function argument does not return an array", () => {
runThrowTest(
`nameof.toArray<MyInterface>(o => o.Prop1);`,
"Unsupported toArray call expression. An array must be returned by the provided function: nameof.toArray<MyInterface>((o) => o.Prop1)",
);
});
it("should throw when no arguments are provided", () => {
runThrowTest(`nameof.toArray<MyInterface>();`, "Unable to parse call expression. No arguments provided: nameof.toArray<MyInterface>()");
});
});
describe("split", () => {
it("should return an array of values where each element is a subsequent part of the path provided", () => {
runTest(`nameof.split<MyInterface>(o => o.Prop1.Prop2.Prop3);`, `["Prop1", "Prop2", "Prop3"];`);
});
it("should return an array of values where each element is a subsequent part of the path provided", () => {
runTest(`nameof.split(o.Prop1.Prop2.Prop3);`, `["o", "Prop1", "Prop2", "Prop3"];`);
});
it("should allow using a period index", () => {
runTest(`nameof.split(MyTest.Test.This, 1);`, `["Test", "This"];`);
});
it("should allow using a period index of 0", () => {
runTest(`nameof.split(MyTest.Test.This, 0);`, `["MyTest", "Test", "This"];`);
});
it("should allow using a period index up to its max value", () => {
runTest(`nameof.split(MyTest.Test.This, 2);`, `["This"];`);
});
it("should allow using a negative period index", () => {
runTest(`nameof.split(MyTest.Test.This, -1);`, `["This"];`);
});
it("should allow using a negative period index to its max value", () => {
runTest(`nameof.split(MyTest.Test.This, -3);`, `["MyTest", "Test", "This"];`);
});
it("should throw when the periodIndex is not a number literal", () => {
runThrowTest(`nameof.split(MyTest.Test, 'test')`, `Expected count to be a number, but was: "test"`);
});
it("should throw when the periodIndex is greater than the number of periods", () => {
runThrowTest(`nameof.split(MyTest.Test, 2)`, "Count of 2 was larger than max count of 1: nameof.split(MyTest.Test, 2)");
});
it("should throw when the absolute value of the negative periodIndex is greater than the number of periods + 1", () => {
runThrowTest(`nameof.split(MyTest.Test, -3)`, "Count of -3 was larger than max count of -2: nameof.split(MyTest.Test, -3)");
});
});
describe("general", () => {
it("should error when specifying a different nameof property", () => {
runThrowTest(`nameof.nonExistent()`, "Unsupported nameof call expression with property 'nonExistent': nameof.nonExistent()");
});
it("should replace handling comments", () => {
const input = `nameof(window);
// nameof(window);
nameof(window);
/* nameof(window); nameof(window); */
nameof(window);
`;
const expected = `"window";
// nameof(window);
"window";
/* nameof(window); nameof(window); */
"window";
`;
runTest(input, expected);
});
it("should replace handling strings", () => {
const input = `nameof(window);
const t = /\`/g;
\`nameof(window); /
\${nameof(window)}
\${nameof(alert)}
nameof(window);
\`; // test
"nameof(window);";
"\\"nameof(window);";
'nameof(window);';
'\\'\\"nameof(window);';
"C:\\\\";
nameof(window);
\`\${() => {
nameof(console);
}}\`;
`;
const expected = `"window";
const t = /\`/g;
\`nameof(window); /
$\{"window"\}
$\{"alert"\}
nameof(window);
\`; // test
"nameof(window);";
"\\"nameof(window);";
"nameof(window);";
"'\\"nameof(window);";
"C:\\\\";
"window";
\`\${() => {
"console";
}}\`;
`;
runTest(input, expected);
});
it("should handle division operators", () => {
const input = `const t = 2 / 1;\nnameof(testing);`;
const expected = `const t = 2 / 1;\n"testing";`;
runTest(input, expected);
});
});
function runTest(text: string, expected: string) {
if (options.commonPrefix != null) {
text = options.commonPrefix + text;
}
const result = getTransformedText(text);
if (!expected.endsWith("\n")) {
expected += "\n";
}
assert.strictEqual(formatter.formatText("file.ts", result), expected);
}
function runThrowTest(text: string, possibleExpectedMessages: string | string[]) {
if (options.commonPrefix != null) {
text = options.commonPrefix + text;
}
let transformedText: string | undefined;
// for some reason, assert.throws was not working
try {
transformedText = getTransformedText(text);
} catch (ex: any) {
possibleExpectedMessages = getPossibleExpectedMessages();
const actualMessage = (ex as any).message;
for (const message of possibleExpectedMessages) {
if (message === actualMessage) {
return;
}
}
throw new Error(
`Expected the error message of ${JSON.stringify(actualMessage)} to equal `
+ `one of the following messages: ${JSON.stringify(possibleExpectedMessages)}`,
);
}
throw new Error(`Expected to throw, but returned: ${transformedText}`);
function getPossibleExpectedMessages() {
const result = getAsArray();
for (let i = result.length - 1; i >= 0; i--) {
const originalText = result[i];
result[i] = "[ts-nameof]: " + originalText;
// ts
result.push("[ts-nameof:/file.ts]: " + originalText);
// babel
const babelPath = path.resolve(__dirname, "../../transforms-babel/src/tests/test.ts");
// todo: temporary... switch to one path in the year 2021 (old versions of babel won't have the prefixed path)
result.push(`${babelPath}: [ts-nameof:${babelPath}]: ${originalText}`);
// babel macro (not ideal, but whatever)
result.push(`${path.resolve(__dirname, "../../ts-nameof.macro/src/tests/test.ts")}: ./ts-nameof.macro: [ts-nameof]: ${originalText}`);
}
return result;
function getAsArray() {
if (typeof possibleExpectedMessages === "string") {
return [possibleExpectedMessages];
}
return possibleExpectedMessages;
}
}
}
}
function getFirstAccessedPropertyMustNotBeComputedErrorText(nodeText: string) {
return `First accessed property must not be computed except if providing a string: ${nodeText}`;
}
function getNotSupportedErrorText(nodeText: string) {
return `The node \`${nodeText}\` is not supported in this scenario.`;
}
function getUnusedNameofInterpolateErrorText(nodeText: string) {
return `Found a nameof.interpolate that did not exist within a nameof.full call expression: nameof.interpolate(${nodeText})`;
}
================================================
FILE: packages/tests-common/tsconfig.json
================================================
{
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "./dist"
},
"extends": "../../tsconfig.common.json",
"include": ["./src/**/*.ts"]
}
================================================
FILE: packages/transforms-babel/.mocharc.yml
================================================
require: ts-node/register
recursive: true
reporter: progress
watch-extensions: ts
timeout: 10000
spec: src/tests/**/*.ts
================================================
FILE: packages/transforms-babel/.npmignore
================================================
tsconfig.json
tsconfig.tsbuildinfo
src
*.log
*.js.map
.mocharc.yml
================================================
FILE: packages/transforms-babel/README.md
================================================
# ts-nameof - Babel transforms
Contains the babel transforms used in ts-nameof.
## Development Commands
```
npm run test
```
================================================
FILE: packages/transforms-babel/package.json
================================================
{
"name": "@ts-nameof/transforms-babel",
"version": "4.2.1",
"description": "ts-nameof - Babel transforms for ts-nameof packages.",
"main": "./dist/index.js",
"author": "David Sherret",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"scripts": {
"clean": "rimraf dist && tsc --b --clean",
"build": "tsc --b",
"test": "npm run build && mocha",
"test:debug": "npm run build && mocha --inspect-brk"
},
"dependencies": {
"@ts-nameof/common": "^4.2.1",
"@ts-nameof/transforms-common": "^4.2.1"
},
"devDependencies": {
"@babel/core": "^7.16.5",
"@babel/preset-typescript": "^7.16.5",
"@babel/types": "^7.16.0",
"@ts-nameof/tests-common": "^4.2.0",
"@types/babel__core": "^7.1.17",
"@types/babel__generator": "^7.6.3",
"@types/babel__template": "^7.4.1",
"@types/babel__traverse": "^7.14.2",
"@types/node": "^17.0.0",
"mocha": "^9.1.3",
"rimraf": "^3.0.2",
"ts-node": "^10.4.0",
"typescript": "^4.5.4"
}
}
================================================
FILE: packages/transforms-babel/src/VisitSourceFileContext.ts
================================================
import { Node } from "@babel/types";
export interface VisitSourceFileContext {
interpolateExpressions: Set<Node>;
}
================================================
FILE: packages/transforms-babel/src/helpers.ts
================================================
import * as babelTypes from "@babel/types";
import { BlockStatement, Node, UnaryExpression } from "@babel/types";
import { throwError } from "@ts-nameof/common";
export function isNegativeNumericLiteral(t: typeof babelTypes, node: Node): node is UnaryExpression {
if (!t.isUnaryExpression(node)) {
return false;
}
return node.operator === "-" && t.isNumericLiteral(node.argument);
}
export function getNegativeNumericLiteralValue(t: typeof babelTypes, node: UnaryExpression) {
if (node.operator !== "-" || !t.isNumericLiteral(node.argument)) {
return throwError("The passed in UnaryExpression must be for a negative numeric literal.");
}
return node.argument.value * -1;
}
export function getReturnStatementArgumentFromBlock(t: typeof babelTypes, block: BlockStatement) {
for (const statement of block.body) {
if (t.isReturnStatement(statement) && statement.argument != null) {
return statement.argument;
}
}
return undefined;
}
================================================
FILE: packages/transforms-babel/src/index.ts
================================================
import * as babel from "@babel/core";
import { Node, NodePath } from "@babel/traverse";
import * as babelTypes from "@babel/types";
import { throwErrorForSourceFile } from "@ts-nameof/common";
import { transformCallExpression } from "@ts-nameof/transforms-common";
import { parse, ParseOptions } from "./parse";
import { transform } from "./transform";
export interface TransformOptions extends ParseOptions {
}
export function plugin({ types: t }: { types: typeof babelTypes }): babel.PluginItem {
const visitor = {
CallExpression(path: NodePath, state: unknown) {
const filePath = (state as any).file.opts.filename as string;
try {
transformNode(t, path, {
// temp assertion because I'm too lazy to investigate what's going on here
traverseChildren: () => path.traverse(visitor as any, state as any),
});
} catch (err: any) {
return throwErrorForSourceFile(err.message, filePath);
}
},
};
return { visitor };
}
export function transformNode(t: typeof babelTypes, path: NodePath, options: TransformOptions = {}) {
const parseResult = parse(t, path, options);
if (parseResult == null) {
return;
}
const transformResult = transform(t, transformCallExpression(parseResult));
// temporary assertion due to conflicting type declaration versions
path.replaceWith(transformResult as Node);
}
================================================
FILE: packages/transforms-babel/src/parse.ts
================================================
import { NodePath } from "@babel/traverse";
import * as babelTypes from "@babel/types";
import {
ArrayExpression,
ArrowFunctionExpression,
BlockStatement,
CallExpression,
Expression,
FunctionExpression,
MemberExpression,
Node,
NumericLiteral,
StringLiteral,
TemplateLiteral,
TSImportType,
TSQualifiedName,
TSTypeParameterInstantiation,
UnaryExpression,
V8IntrinsicIdentifier,
} from "@babel/types";
import { throwError } from "@ts-nameof/common";
import * as common from "@ts-nameof/transforms-common";
import { getNegativeNumericLiteralValue, getReturnStatementArgumentFromBlock, isNegativeNumericLiteral } from "./helpers";
export interface ParseOptions {
/**
* Action to prompt the children to be traversed. This is to allow traversing the nodes in post order.
*/
traverseChildren?: () => void;
/**
* Expected identifier name at the start of the call expression. This could be different when using a macro.
* @default Defaults to "nameof".
*/
nameofIdentifierName?: string;
}
/**
* Parses a Babel AST node to a common NameofCallExpression or returns undefined if the current node
* is not a nameof call expression.
* @param t - Babel types namespace to use.
* @param path - Path of the current Babel AST node.
* @param options - Options for parsing.
* @remarks Parsing to a common structure allows for the same code to be used to determine the final string.
*/
export function parse(t: typeof babelTypes, path: NodePath, options: ParseOptions = {}) {
if (!isNameof(path.node)) {
return undefined;
}
if (options.traverseChildren) {
options.traverseChildren(); // tell the caller to go over the nodes in post order
}
const propertyName = parsePropertyName(path.node);
// ignore nameof.interpolate function calls... they will be dealt with later
if (isInterpolatePropertyName(propertyName)) {
handleNameofInterpolate(path.node);
return undefined;
}
return parseNameof(path.node);
function parseNameof(callExpr: CallExpression): common.NameofCallExpression {
return {
property: propertyName,
typeArguments: parseTypeArguments(callExpr),
arguments: parseArguments(callExpr),
};
}
function parsePropertyName(callExpr: CallExpression) {
const { callee } = callExpr;
if (!t.isMemberExpression(callee) || !t.isIdentifier(callee.property)) {
return undefined;
}
return callee.property.name;
}
function parseTypeArguments(callExpr: CallExpression) {
// babel uses incorrect naming. these are type arguments
const typeArguments = (callExpr as any).typeParameters as TSTypeParameterInstantiation | undefined;
if (typeArguments == null) {
return [];
}
return typeArguments.params.map(arg => parseCommonNode(arg));
}
function parseArguments(callExpr: CallExpression) {
return callExpr.arguments.map(arg => parseCommonNode(arg));
}
function parseCommonNode(node: Node): common.Node {
if (t.isMemberExpression(node)) {
return parseMemberExpression(node);
}
if (t.isArrowFunctionExpression(node)) {
return parseFunctionReturnExpression(node, getArrowFunctionReturnExpression(node));
}
if (t.isFunctionExpression(node)) {
return parseFunctionReturnExpression(node, getReturnStatementArgumentFromBlockOrThrow(node.body));
}
if (t.isTSNonNullExpression(node) || t.isParenthesizedExpression(node) || t.isTSAsExpression(node)) {
return parseCommonNode(node.expression);
}
if (t.isTSQualifiedName(node)) {
return parseQualifiedName(node);
}
if (t.isTSTypeReference(node)) {
return parseCommonNode(node.typeName);
}
if (t.isSpreadElement(node)) {
return parseCommonNode(node.argument);
}
if (t.isNumericLiteral(node) || isNegativeNumericLiteral(t, node)) {
return parseNumeric(node);
}
if (t.isStringLiteral(node)) {
return parseStringLiteral(node);
}
if (t.isIdentifier(node)) {
return parseIdentifier(node);
}
if (t.isArrayExpression(node)) {
return parseArrayExpression(node);
}
if (t.isThisExpression(node)) {
return common.createIdentifierNode("this");
}
if (t.isSuper(node)) {
return common.createIdentifierNode("super");
}
if (t.isTSImportType(node)) {
return parseImportType(node, false);
}
if (t.isTSTypeQuery(node) && t.isTSImportType(node.exprName)) {
return parseImportType(node.exprName, true);
}
if (t.isTSLiteralType(node)) {
return parseCommonNode(node.literal); // skip over and go straight to the literal
}
if (t.isTemplateLiteral(node)) {
return parseTemplateExpression(node);
}
if (isNameof(node) && isInterpolatePropertyName(parsePropertyName(node))) {
return parseInterpolateNode(node);
}
return throwError(`Unhandled node type (${node.type}) in text: ${getNodeText(node)} (Please open an issue if you believe this should be supported.)`);
}
function parseArrayExpression(node: ArrayExpression) {
const result: common.Node[] = [];
node.elements.forEach(element => {
if (element == null) {
return throwError(`Unsupported scenario with empty element encountered in array: ${getNodeText(node)}`);
}
result.push(parseCommonNode(element));
});
return common.createArrayLiteralNode(result);
}
function parseMemberExpression(node: MemberExpression) {
const expressionCommonNode = parseCommonNode(node.object);
const nameCommonNode = parseCommonNode(node.property);
const computedCommonNode = node.computed ? common.createComputedNode(nameCommonNode) : undefined;
getEndCommonNode(expressionCommonNode).next = computedCommonNode || nameCommonNode;
return expressionCommonNode;
}
function parseQualifiedName(node: TSQualifiedName) {
const leftCommonNode = parseCommonNode(node.left);
const rightCommonNode = parseCommonNode(node.right);
getEndCommonNode(leftCommonNode).next = rightCommonNode;
return leftCommonNode;
}
function parseNumeric(node: NumericLiteral | UnaryExpression) {
return common.createNumericLiteralNode(getNodeValue());
function getNodeValue() {
if (t.isNumericLiteral(node)) {
return node.value;
}
return getNegativeNumericLiteralValue(t, node);
}
}
function parseStringLiteral(node: StringLiteral) {
return common.createStringLiteralNode(node.value);
}
function parseIdentifier(node: Node) {
const text = getIdentifierTextOrThrow(node);
return common.createIdentifierNode(text);
}
function parseFunctionReturnExpression(functionNode: FunctionExpression | ArrowFunctionExpression, node: Expression) {
const parameterNames = functionNode.params.map(p => {
if (t.isIdentifier(p)) {
return p.name;
}
return getNodeText(p);
});
return common.createFunctionNode(parseCommonNode(node), parameterNames);
}
function parseImportType(node: TSImportType, isTypeOf: boolean) {
const importTypeNode = common.createImportTypeNode(isTypeOf, parseCommonNode(node.argument));
const qualifier = node.qualifier == null ? undefined : parseCommonNode(node.qualifier);
getEndCommonNode(importTypeNode).next = qualifier;
return importTypeNode;
}
function parseTemplateExpression(node: TemplateLiteral) {
return common.createTemplateExpressionNode(getParts());
function getParts() {
const parts: (string | common.InterpolateNode)[] = [];
// the number of quasis will always be greater than the number of expressions
for (let i = 0; i < node.quasis.length; i++) {
parts.push(node.quasis[i].value.raw);
const expression = node.expressions[i];
if (expression != null) {
parts.push(common.createInterpolateNode(expression, getNodeText(expression)));
}
}
return parts;
}
}
function parseInterpolateNode(node: CallExpression) {
if (node.arguments.length !== 1) {
return throwError(`Expected a single argument for the nameof.interpolate function call ${getNodeText(node.arguments[0])}.`);
}
return common.createInterpolateNode(node.arguments[0], getNodeText(node.arguments[0]));
}
function getEndCommonNode(commonNode: common.Node) {
while (commonNode.next != null) {
commonNode = commonNode.next;
}
return commonNode;
}
function getArrowFunctionReturnExpression(func: ArrowFunctionExpression) {
if (t.isBlock(func.body)) {
return getReturnStatementArgumentFromBlockOrThrow(func.body);
}
return func.body;
}
function getIdentifierTextOrThrow(node: Node) {
if (!t.isIdentifier(node)) {
return throwError(`Expected node to be an identifier: ${getNodeText(node)}`);
}
return node.name;
}
function getReturnStatementArgumentFromBlockOrThrow(block: BlockStatement) {
return getReturnStatementArgumentFromBlock(t, block)
|| throwError(`Cound not find return statement with an expression in function expression: ${getNodeText(block)}`);
}
function getNodeText(node: Node) {
const outerNodeStart = path.node.start!;
const innerNodeStart = node.start!;
const offset = innerNodeStart - outerNodeStart;
return path.getSource().substr(offset, node.end! - node.start!);
}
function isNameof(node: Node): node is CallExpression {
if (!t.isCallExpression(node)) {
return false;
}
const identifier = getIdentifierToInspect(node.callee);
return identifier != null && identifier.name === (options.nameofIdentifierName || "nameof");
function getIdentifierToInspect(expression: Expression | V8IntrinsicIdentifier) {
if (t.isIdentifier(expression)) {
return expression;
}
if (t.isMemberExpression(expression) && t.isIdentifier(expression.object)) {
return expression.object;
}
return undefined;
}
}
function handleNameofInterpolate(callExpr: CallExpression) {
if (!hasAncestorNameofFull()) {
return throwError(
`Found a nameof.interpolate that did not exist within a `
+ `nameof.full call expression: ${getNodeText(callExpr)}`,
);
}
if (callExpr.arguments.length !== 1) {
return throwError("Unexpected scenario where a nameof.interpolate function did not have a single argument.");
}
function hasAncestorNameofFull() {
let parentPath: NodePath | null | undefined = path.parentPath;
while (parentPath != null) {
if (isNameof(parentPath.node) && parsePropertyName(parentPath.node) === "full") {
return true;
}
parentPath = parentPath.parentPath;
}
return false;
}
}
function isInterpolatePropertyName(propertyName: string | undefined) {
return propertyName === "interpolate";
}
}
================================================
FILE: packages/transforms-babel/src/tests/pluginTests.ts
================================================
import * as babel from "@babel/core";
import "@babel/preset-typescript";
import { runCommonTests } from "@ts-nameof/tests-common";
import * as path from "path";
import { plugin } from "../index";
runCommonTests(run);
function run(text: string) {
return babel.transformSync(text, {
presets: [
"@babel/preset-typescript",
],
plugins: [
plugin,
],
filename: path.resolve(__dirname, "test.ts"),
ast: false,
generatorOpts: {
retainLines: true,
},
})!.code!;
}
================================================
FILE: packages/transforms-babel/src/transform.ts
================================================
import * as babelTypes from "@babel/types";
import { throwError } from "@ts-nameof/common";
import * as common from "@ts-nameof/transforms-common";
/**
* Transforms a common node to a Babel node.
* @param node Common node to be transformed.
*/
export function transform(t: typeof babelTypes, node: common.Node): babelTypes.StringLiteral | babelTypes.ArrayExpression | babelTypes.TemplateLiteral {
switch (node.kind) {
case "StringLiteral":
return t.stringLiteral(node.value);
case "ArrayLiteral":
return t.arrayExpression(node.elements.map(element => transform(t, element)));
case "TemplateExpression":
return createTemplateLiteral(t, node);
default:
return throwError(`Unsupported node kind: ${node.kind}`);
}
}
function createTemplateLiteral(t: typeof babelTypes, node: common.TemplateExpressionNode) {
const quasis: babelTypes.TemplateElement[] = [];
const expressions: babelTypes.Expression[] = [];
for (const part of node.parts) {
if (typeof part === "string") {
quasis.push(t.templateElement({
// I believe for the use case of this library, both the raw and cooked can be the same, but adding this
// just in case for the future...
raw: getRawValue(part),
// Need to add this for @babel/preset-env.
cooked: part,
}));
} else {
const expr = part.expression as babelTypes.Expression;
expressions.push(expr);
}
}
// set the last quasi as the tail
quasis[quasis.length - 1].tail = true;
return t.templateLiteral(quasis, expressions);
function getRawValue(text: string) {
// From
// Adds a backslash before every `, \ and ${
return text.replace(/\\|`|\${/g, "\\$&");
}
}
================================================
FILE: packages/transforms-babel/tsconfig.json
================================================
{
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "./dist"
},
"extends": "../../tsconfig.common.json",
"include": ["./src"],
"references": [
{ "path": "../common" },
{ "path": "../transforms-common" },
{ "path": "../tests-common" }
]
}
================================================
FILE: packages/transforms-common/.mocharc.yml
================================================
require: ts-node/register
recursive: true
reporter: progress
watch-extensions: ts
timeout: 10000
spec: src/tests/**/*.ts
================================================
FILE: packages/transforms-common/.npmignore
================================================
tsconfig.json
tsconfig.tsbuildinfo
src
dist/tests
.mocharc.yml
*.log
*.js.map
================================================
FILE: packages/transforms-common/README.md
================================================
# ts-nameof - Transforms Common
Contains the code shared between babel and typescript transforms.
================================================
FILE: packages/transforms-common/package.json
================================================
{
"name": "@ts-nameof/transforms-common",
"version": "4.2.1",
"description": "ts-nameof - Common code for transforms.",
"main": "./dist/index.js",
"author": "David Sherret",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"scripts": {
"clean": "rimraf dist && tsc --b --clean",
"build": "tsc --b",
"test": "npm run build && mocha",
"test:debug": "npm run build && mocha --inspect-brk"
},
"dependencies": {
"@ts-nameof/common": "^4.2.1"
},
"devDependencies": {
"@types/mocha": "^9.0.0",
"@types/node": "^17.0.0",
"mocha": "^9.1.3",
"rimraf": "^3.0.2",
"ts-node": "^10.4.0",
"typescript": "^4.5.4"
}
}
================================================
FILE: packages/transforms-common/src/StringOrTemplateExpressionBuilder.ts
================================================
import { createStringLiteralNode, createTemplateExpressionNode } from "./nodeFactories";
import { InterpolateNode, StringLiteralNode, TemplateExpressionNode } from "./nodes";
/**
* Builds up a string that will be a string literal if able, but will change to a template
* expression if necessary.
*/
export class StringOrTemplateExpressionNodeBuilder {
private text: string | undefined = "";
private items: (string | InterpolateNode)[] = [];
hasText() {
return this.text != null && this.text.length > 0 || this.items.length > 0;
}
buildNode(): StringLiteralNode | TemplateExpressionNode {
if (this.text != null) {
return createStringLiteralNode(this.text);
}
return createTemplateExpressionNode(this.items);
}
addItem(item: string | InterpolateNode | StringLiteralNode | TemplateExpressionNode) {
if (typeof item === "string") {
this.addText(item);
} else if (item.kind === "StringLiteral") {
this.addText(item.value);
} else if (item.kind === "TemplateExpression") {
for (const part of item.parts) {
this.addItem(part);
}
} else {
this.addInterpolate(item);
}
}
addText(newText: string) {
if (this.text == null) {
if (typeof this.items[this.items.length - 1] === "string") {
this.items[this.items.length - 1] += newText;
} else {
this.items.push(newText);
}
} else {
this.text += newText;
}
}
private addInterpolate(interpolate: InterpolateNode) {
if (this.text != null) {
this.items.push(this.text);
this.text = undefined;
}
this.items.push(interpolate);
}
}
================================================
FILE: packages/transforms-common/src/index.ts
================================================
export * from "./nodeFactories";
export * from "./nodes";
export * from "./transformCallExpression";
================================================
FILE: packages/transforms-common/src/nodeFactories.ts
================================================
import {
ArrayLiteralNode,
ComputedNode,
FunctionNode,
IdentifierNode,
ImportTypeNode,
InterpolateNode,
Node,
NumericLiteralNode,
StringLiteralNode,
TemplateExpressionNode,
} from "./nodes";
export function createIdentifierNode(value: string, next?: Node | undefined): IdentifierNode {
return {
kind: "Identifier",
value,
next,
};
}
export function createStringLiteralNode(value: string, next?: Node | undefined): StringLiteralNode {
return {
kind: "StringLiteral",
value,
next,
};
}
export function createNumericLiteralNode(value: number, next?: Node | undefined): NumericLiteralNode {
return {
kind: "NumericLiteral",
value,
next,
};
}
export function createArrayLiteralNode(elements: ArrayLiteralNode["elements"], next?: Node | undefined): ArrayLiteralNode {
return {
kind: "ArrayLiteral",
elements,
next,
};
}
export function createComputedNode(value: Node, next?: Node | undefined): ComputedNode {
return {
kind: "Computed",
value,
next,
};
}
export function createFunctionNode(value: Node, parameterNames: string[], next?: Node | undefined): FunctionNode {
return {
kind: "Function",
parameterNames,
value,
next,
};
}
export function createImportTypeNode(isTypeOf: boolean, argument: Node | undefined, next?: Node | undefined): ImportTypeNode {
return {
kind: "ImportType",
isTypeOf,
argument,
next,
};
}
export function createTemplateExpressionNode(parts: (string | InterpolateNode)[], next?: Node | undefined): TemplateExpressionNode {
return {
kind: "TemplateExpression",
parts,
next,
};
}
export function createInterpolateNode(expression: unknown, expressionText: string, next?: Node | undefined): InterpolateNode {
return {
kind: "Interpolate",
expression,
expressionText,
next,
};
}
================================================
FILE: packages/transforms-common/src/nodeHelpers.ts
================================================
import { Node } from "./nodes";
export function flattenNodeToArray(node: Node) {
const flattenedNodes: Node[] = [node];
while (node.next != null) {
flattenedNodes.push(node.next);
node = node.next;
}
return flattenedNodes;
}
export function getLastNextNode(node: Node) {
while (node.next != null) {
node = node.next;
}
return node;
}
================================================
FILE: packages/transforms-common/src/nodes.ts
================================================
// common AST to share between babel and typescript
export interface NameofCallExpression {
property: string | undefined;
typeArguments: Node[];
arguments: Node[];
}
export type Node =
| IdentifierNode
| StringLiteralNode
| NumericLiteralNode
| ArrayLiteralNode
| ComputedNode
| FunctionNode
| ImportTypeNode
| TemplateExpressionNode
| InterpolateNode;
export interface IdentifierNode {
kind: "Identifier";
value: string;
next: Node | undefined;
}
export interface StringLiteralNode {
kind: "StringLiteral";
value: string;
next: Node | undefined;
}
export interface NumericLiteralNode {
kind: "NumericLiteral";
value: number;
next: Node | undefined;
}
export interface ArrayLiteralNode {
kind: "ArrayLiteral";
elements: Node[];
next: Node | undefined;
}
/**
* Node surrounded in brackets.
* Ex. `[4]` in `obj[4]`
*/
export interface ComputedNode {
kind: "Computed";
value: Node;
next: Node | undefined;
}
export interface FunctionNode {
kind: "Function";
parameterNames: string[];
value: Node;
next: Node | undefined;
}
export interface ImportTypeNode {
kind: "ImportType";
isTypeOf: boolean;
argument: Node | undefined;
next: Node | undefined;
}
export interface TemplateExpressionNode {
kind: "TemplateExpression";
parts: (string | InterpolateNode)[];
next: Node | undefined;
}
/**
* An interpolate node.
* Ex. Created from call expressions such as: `nameof.interpolate(expression)`
*/
export interface InterpolateNode {
kind: "Interpolate";
/** The original AST node. */
expression: unknown;
/** The expression text for printing purposes. */
expressionText: string;
next: Node | undefined;
}
================================================
FILE: packages/transforms-common/src/printers.ts
================================================
import { assertNever } from "@ts-nameof/common";
import { NameofCallExpression, Node, TemplateExpressionNode } from "./nodes";
/**
* Prints the call expression to a string. Useful for displaying diagnostic information to the user.
* @param callExpr `nameof` call expression to print.
*/
export function printCallExpression(callExpr: NameofCallExpression) {
let result = "nameof";
writePropertyName();
if (callExpr.typeArguments.length > 0) {
writeTypeArguments();
}
writeArguments();
return result;
function writePropertyName() {
if (callExpr.property != null) {
result += `.${callExpr.property}`;
}
}
function writeTypeArguments() {
result += "<";
for (let i = 0; i < callExpr.typeArguments.length; i++) {
if (i > 0) {
result += ", ";
}
result += printNode(callExpr.typeArguments[i]);
}
result += ">";
}
function writeArguments() {
result += "(";
for (let i = 0; i < callExpr.arguments.length; i++) {
if (i > 0) {
result += ", ";
}
result += printNode(callExpr.arguments[i]);
}
result += ")";
}
}
/**
* Prints a node to a string. Useful for displaying diagnostic information to the user.
* @param node Node to print.
*/
export function printNode(node: Node): string {
// todo: this should throw in more scenarios (ex. string literal after an identifier)
let result = getCurrentText();
if (node.next != null) {
if (node.next.kind === "Identifier") {
result += "." + printNode(node.next);
} else {
result += printNode(node.next);
}
}
return result;
function getCurrentText() {
switch (node.kind) {
case "StringLiteral":
return `\"${node.value}\"`;
case "NumericLiteral":
return node.value.toString();
case "Identifier":
return node.value;
case "Computed":
return `[${printNode(node.value)}]`;
case "Function":
let functionResult = `(${node.parameterNames.join(", ")}) => ${printNode(node.value)}`;
if (node.next != null) {
functionResult = `(${functionResult})`;
}
return functionResult;
case "ArrayLiteral":
return `[${node.elements.map(e => printNode(e)).join(", ")}]`;
case "ImportType":
return (node.isTypeOf ? "typeof " : "") + `import(${node.argument == null ? "" : printNode(node.argument)})`;
case "Interpolate":
return `nameof.interpolate(${node.expressionText})`;
case "TemplateExpression":
return printTemplateExpression(node);
default:
return assertNever(node, `Unhandled kind: ${(node as Node).kind}`);
}
}
function printTemplateExpression(TemplateExpression: TemplateExpressionNode) {
let text = "`";
for (const part of TemplateExpression.parts) {
if (typeof part === "string") {
text += part;
} else {
text += "${" + printNode(part) + "}";
}
}
text += "`";
return text;
}
}
================================================
FILE: packages/transforms-common/src/tests/printerTests.ts
================================================
import * as assert from "assert";
import * as factories from "../nodeFactories";
import { NameofCallExpression, Node } from "../nodes";
import * as printers from "../printers";
describe("printCallExpression", () => {
function doTest(callExpr: NameofCallExpression, expectedText: string) {
const result = printers.printCallExpression(callExpr);
assert.equal(result, expectedText);
}
it("should print a basic call expression", () => {
doTest({
property: undefined,
typeArguments: [],
arguments: [],
}, "nameof()");
});
it("should print with a property", () => {
doTest({
property: "full",
typeArguments: [],
arguments: [],
}, "nameof.full()");
});
it("should print with an argument", () => {
doTest({
property: undefined,
typeArguments: [],
arguments: [factories.createIdentifierNode("test")],
}, "nameof(test)");
});
it("should print with arguments", () => {
doTest({
property: undefined,
typeArguments: [],
arguments: [
factories.createIdentifierNode("test1"),
factories.createIdentifierNode("test2"),
],
}, "nameof(test1, test2)");
});
it("should print with a type argument", () => {
doTest({
property: undefined,
typeArguments: [factories.createIdentifierNode("T")],
arguments: [],
}, "nameof<T>()");
});
it("should print with type arguments", () => {
doTest({
property: undefined,
typeArguments: [
factories.createIdentifierNode("T"),
factories.createIdentifierNode("U"),
],
arguments: [],
}, "nameof<T, U>()");
});
it("should print with everything", () => {
doTest({
property: "full",
typeArguments: [
factories.createIdentifierNode("T"),
factories.createIdentifierNode("U"),
],
arguments: [
factories.createIdentifierNode("test1"),
factories.createIdentifierNode("test2"),
],
}, "nameof.full<T, U>(test1, test2)");
});
});
describe("printNode", () => {
function doTest(node: Node, expectedText: string) {
const result = printers.printNode(node);
assert.equal(result, expectedText);
}
describe("identifier", () => {
it("should print an identifier", () => {
doTest(factories.createIdentifierNode("Test"), "Test");
});
it("should print the next identifier separated by a period", () => {
doTest(factories.createIdentifierNode("Test", factories.createIdentifierNode("Next")), "Test.Next");
});
it("should print the next computed value with no separation", () => {
const node = factories.createIdentifierNode("Test", factories.createComputedNode(factories.createStringLiteralNode("prop")));
doTest(node, `Test["prop"]`);
});
});
describe("string literal", () => {
it("should print in quotes", () => {
doTest(factories.createStringLiteralNode("test"), `"test"`);
});
it("should print with a property after", () => {
const node = factories.createStringLiteralNode("test", factories.createIdentifierNode("length"));
doTest(node, `"test".length`);
});
});
describe("numeric literal", () => {
it("should print", () => {
doTest(factories.createNumericLiteralNode(5), `5`);
});
it("should print with a property after", () => {
const node = factories.createNumericLiteralNode(5, factories.createIdentifierNode("length"));
doTest(node, `5.length`);
});
});
describe("computed", () => {
it("should print inside brackets", () => {
const node = factories.createComputedNode(factories.createStringLiteralNode("test"));
doTest(node, `["test"]`);
});
it("should print with a property after", () => {
const node = factories.createComputedNode(factories.createNumericLiteralNode(5), factories.createIdentifierNode("length"));
doTest(node, `[5].length`);
});
});
describe("function", () => {
it("should print with no arguments", () => {
const node = factories.createFunctionNode(factories.createNumericLiteralNode(5), []);
doTest(node, `() => 5`);
});
it("should print with an argument", () => {
const node = factories.createFunctionNode(factories.createNumericLiteralNode(5), ["p"]);
doTest(node, `(p) => 5`); // keep it simple (don't bother removing parens)
});
it("should print with arguments", () => {
const node = factories.createFunctionNode(factories.createNumericLiteralNode(5), ["a", "b"]);
doTest(node, `(a, b) => 5`);
});
it("should print with a property after", () => {
const node = factories.createFunctionNode(factories.createNumericLiteralNode(5), ["a", "b"], factories.createIdentifierNode("length"));
doTest(node, `((a, b) => 5).length`);
});
});
describe("array", () => {
it("should print the array with no elements", () => {
const node = factories.createArrayLiteralNode([]);
doTest(node, "[]");
});
it("should print the array with one element", () => {
const node = factories.createArrayLiteralNode([factories.createStringLiteralNode("test")]);
doTest(node, `["test"]`);
});
it("should print the array with multiple elements", () => {
const node = factories.createArrayLiteralNode([factories.createStringLiteralNode("test"), factories.createStringLiteralNode("test2")]);
doTest(node, `["test", "test2"]`);
});
it("should print with a property after", () => {
const node = factories.createArrayLiteralNode([], factories.createIdentifierNode("length"));
doTest(node, `[].length`);
});
});
describe("import type", () => {
it("should print when it has no argument", () => {
const node = factories.createImportTypeNode(false, undefined, factories.createIdentifierNode("length"));
doTest(node, `import().length`);
});
it("should print when it receives an identifier", () => {
const node = factories.createImportTypeNode(false, factories.createIdentifierNode("test"), undefined);
doTest(node, `import(test)`);
});
it("should print when it receives a string literal", () => {
const node = factories.createImportTypeNode(false, factories.createStringLiteralNode("test"), undefined);
doTest(node, `import("test")`);
});
it("should print when it has a typeof", () => {
const node = factories.createImportTypeNode(true, factories.createIdentifierNode("test"));
doTest(node, `typeof import(test)`);
});
});
describe("template literal", () => {
it("should print when only has a string", () => {
const node = factories.createTemplateExpressionNode(["testing"], factories.createIdentifierNode("length"));
doTest(node, "`testing`.length");
});
it("should print when also has an interpolate node", () => {
const node = factories.createTemplateExpressionNode(["testing", factories.createInterpolateNode(undefined, "myVar"), "this"]);
// in practice, the printer will never be printing a template literal
doTest(node, "`testing${nameof.interpolate(myVar)}this`");
});
});
describe("interpolate node", () => {
it("should print", () => {
const node = factories.createInterpolateNode(undefined, "myVar", factories.createIdentifierNode("length"));
doTest(node, "nameof.interpolate(myVar).length");
});
});
});
================================================
FILE: packages/transforms-common/src/transformCallExpression.ts
================================================
import { assertNever, throwError } from "@ts-nameof/common";
import { createArrayLiteralNode, createStringLiteralNode, createTemplateExpressionNode } from "./nodeFactories";
import { flattenNodeToArray, getLastNextNode } from "./nodeHelpers";
import { FunctionNode, NameofCallExpression, Node, StringLiteralNode, TemplateExpressionNode } from "./nodes";
import { printCallExpression, printNode } from "./printers";
import { StringOrTemplateExpressionNodeBuilder } from "./StringOrTemplateExpressionBuilder";
export function transformCallExpression(callExpr: NameofCallExpression) {
if (callExpr.property == null) {
return handleNameof(callExpr);
}
if (callExpr.property === "full") {
return handleNameofFull(callExpr);
}
if (callExpr.property === "toArray") {
return handleNameofToArray(callExpr);
}
if (callExpr.property === "split") {
return handleNameofSplit(callExpr);
}
return throwError(`Unsupported nameof call expression with property '${callExpr.property}': ${printCallExpression(callExpr)}`);
}
function handleNameof(callExpr: NameofCallExpression) {
return parseNameofExpression(getExpression());
function getExpression() {
if (callExpr.arguments.length === 1) {
return callExpr.arguments[0];
} else if (callExpr.typeArguments.length === 1) {
return callExpr.typeArguments[0];
}
return throwError(`Call expression must have one argument or type argument: ${printCallExpression(callExpr)}`);
}
}
function handleNameofFull(callExpr: NameofCallExpression) {
return parseNameofFullExpression(getNodesFromCallExpression(callExpr));
}
function handleNameofSplit(callExpr: NameofCallExpression) {
const literalNodes = getNodesFromCallExpression(callExpr).map(node => parseNode(node));
return createArrayLiteralNode(literalNodes);
}
function handleNameofToArray(callExpr: NameofCallExpression) {
const arrayArguments = getNodeArray();
return createArrayLiteralNode(arrayArguments.map(element => parseNameofExpression(element)));
function getNodeArray() {
if (callExpr.arguments.length === 0) {
return throwError(`Unable to parse call expression. No arguments provided: ${printCallExpression(callExpr)}`);
}
const firstArgument = callExpr.arguments[0];
if (callExpr.arguments.length === 1 && firstArgument.kind === "Function") {
return handleFunction(firstArgument);
} else {
return callExpr.arguments;
}
function handleFunction(func: FunctionNode) {
const functionReturnValue = func.value;
if (functionReturnValue == null || functionReturnValue.kind !== "ArrayLiteral") {
return throwError(`Unsupported toArray call expression. An array must be returned by the provided function: ${printCallExpression(callExpr)}`);
}
return functionReturnValue.elements;
}
}
}
function getNodesFromCallExpression(callExpr: NameofCallExpression) {
const { expression, count } = getExpressionAndCount();
return getNodesFromCount(flattenNodeToArray(expression), count);
function getExpressionAndCount() {
if (shouldUseArguments()) {
return {
expression: getArgumentExpression(),
count: getCountFromNode(callExpr.arguments.length > 1 ? callExpr.arguments[1] : undefined),
};
}
if (callExpr.typeArguments.length > 0) {
return {
expression: callExpr.typeArguments[0],
count: getCountFromNode(callExpr.arguments.length > 0 ? callExpr.arguments[0] : undefined),
};
}
return throwError(`Unsupported use of nameof.full: ${printCallExpression(callExpr)}`);
function shouldUseArguments() {
if (callExpr.arguments.length === 0) {
return false;
}
if (callExpr.typeArguments.length === 0) {
return true;
}
return callExpr.arguments[0].kind === "Function";
}
function getArgumentExpression() {
let expression = callExpr.arguments[0];
if (expression.kind === "Function") {
expression = expression.value;
// skip over the first identifier (ex. skip over `obj` in `obj => obj.test`)
if (expression.next == null) {
return throwError(`A property must be accessed on the object: ${printNode(callExpr.arguments[0])}`);
}
expression = expression.next;
}
return expression;
}
function getCountFromNode(countExpr: Node | undefined) {
if (countExpr == null) {
return 0;
}
if (countExpr.kind !== "NumericLiteral") {
return throwError(`Expected count to be a number, but was: ${printNode(countExpr)}`);
}
return countExpr.value;
}
}
function getNodesFromCount(nodes: Node[], count: number) {
if (count > 0) {
if (count > nodes.length - 1) {
return throwError(`Count of ${count} was larger than max count of ${nodes.length - 1}: ${printCallExpression(callExpr)}`);
}
return nodes.slice(count);
}
if (count < 0) {
if (Math.abs(count) > nodes.length) {
return throwError(`Count of ${count} was larger than max count of ${nodes.length * -1}: ${printCallExpression(callExpr)}`);
}
return nodes.slice(nodes.length + count);
}
return nodes;
}
}
function parseNameofExpression(expression: Node) {
return parseNode(getNodeForNameOf(), expression);
function getNodeForNameOf() {
const node = getLastNextNode(expression);
if (node.kind === "Function") {
const argument = node.value;
if (argument.next == null) {
return throwError(`A property must be accessed on the object: ${printNode(expression)}`);
}
return getLastNextNode(argument.next);
}
return node;
}
}
function parseNode(node: Node, parent?: Node) {
switch (node.kind) {
case "Identifier":
return createStringLiteralNode(node.value);
case "StringLiteral":
// make a copy
return createStringLiteralNode(node.value);
case "TemplateExpression":
// todo: test this
return createTemplateExpressionNode(node.parts);
case "NumericLiteral":
// make a copy
return createStringLiteralNode(node.value.toString());
case "Function":
return throwError(`Nesting functions is not supported: ${printNode(parent || node)}`);
case "Computed":
if (node.value.kind === "StringLiteral" && node.value.next == null) {
return createStringLiteralNode(node.value.value);
}
return throwError(`First accessed property must not be computed except if providing a string: ${printNode(parent || node)}`);
case "Interpolate":
case "ArrayLiteral":
case "ImportType":
return throwNotSupportedErrorForNode(node);
default:
return assertNever(node, `Not implemented node: ${JSON.stringify(node)}`);
}
}
function parseNameofFullExpression(expressionNodes: Node[]): StringLiteralNode | TemplateExpressionNode {
const nodeBuilder = new StringOrTemplateExpressionNodeBuilder();
for (let i = 0; i < expressionNodes.length; i++) {
const node = expressionNodes[i];
if (i > 0 && node.kind === "Identifier") {
nodeBuilder.addText(".");
}
addNodeToBuilder(node);
}
return nodeBuilder.buildNode();
function addNodeToBuilder(node: Node) {
switch (node.kind) {
case "Identifier":
nodeBuilder.addText(node.value);
break;
case "Computed":
nodeBuilder.addText("[");
const computedNodes = flattenNodeToArray(node.value);
for (let i = 0; i < computedNodes.length; i++) {
const computedNode = computedNodes[i];
if (computedNode.kind === "StringLiteral") {
nodeBuilder.addText(`"${computedNode.value}"`);
} else {
if (i > 0 && computedNode.kind === "Identifier") {
nodeBuilder.addText(".");
}
addNodeToBuilder(computedNode);
}
}
nodeBuilder.addText("]");
break;
case "TemplateExpression":
case "StringLiteral":
nodeBuilder.addItem(node);
break;
case "NumericLiteral":
nodeBuilder.addText(node.value.toString());
break;
case "Interpolate":
nodeBuilder.addItem(node);
break;
case "ArrayLiteral":
case "ImportType":
case "Function":
return throwNotSupportedErrorForNode(node);
default:
return assertNever(node, `Not implemented node: ${JSON.stringify(node)}`);
}
}
}
function throwNotSupportedErrorForNode(node: Node) {
return throwError(`The node \`${printNode(node)}\` is not supported in this scenario.`);
}
================================================
FILE: packages/transforms-common/tsconfig.json
================================================
{
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "./dist"
},
"extends": "../../tsconfig.common.json",
"include": ["./src/**/*.ts"],
"references": [
{ "path": "../common" }
]
}
================================================
FILE: packages/transforms-ts/.mocharc.yml
================================================
require: ts-node/register
recursive: true
reporter: progress
watch-extensions: ts
timeout: 10000
spec: src/tests/**/*.ts
================================================
FILE: packages/transforms-ts/.npmignore
================================================
tsconfig.json
tsconfig.tsbuildinfo
src
dist/tests
.mocharc.yml
*.log
*.js.map
================================================
FILE: packages/transforms-ts/README.md
================================================
# ts-nameof - TypeScript transforms
Contains the TypeScript Compiler API transforms used in ts-nameof.
## Development Commands
```
npm run test
```
================================================
FILE: packages/transforms-ts/package.json
================================================
{
"name": "@ts-nameof/transforms-ts",
"version": "4.2.1",
"description": "ts-nameof - TypeScript compiler transforms for ts-nameof packages.",
"main": "./dist/index.js",
"author": "David Sherret",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"scripts": {
"clean": "rimraf dist && tsc --b --clean",
"build": "tsc --b",
"test": "npm run build && mocha",
"test:debug": "npm run build && mocha --inspect-brk"
},
"dependencies": {
"@ts-nameof/common": "^4.2.1",
"@ts-nameof/transforms-common": "^4.2.1"
},
"devDependencies": {
"@ts-nameof/tests-common": "^4.2.0",
"mocha": "^9.1.3",
"rimraf": "^3.0.2",
"ts-node": "^10.4.0",
"typescript": "^4.5.4"
}
}
================================================
FILE: packages/transforms-ts/src/VisitSourceFileContext.ts
================================================
import * as ts from "typescript";
export interface VisitSourceFileContext {
interpolateExpressions: Set<ts.Node>;
}
================================================
FILE: packages/transforms-ts/src/helpers.ts
================================================
import { throwError } from "@ts-nameof/common";
import * as ts from "typescript";
export function isNegativeNumericLiteral(node: ts.Node): node is ts.PrefixUnaryExpression {
if (!ts.isPrefixUnaryExpression(node)) {
return false;
}
return node.operator === ts.SyntaxKind.MinusToken
&& ts.isNumericLiteral(node.operand);
}
export function getNegativeNumericLiteralValue(node: ts.PrefixUnaryExpression) {
if (node.operator !== ts.SyntaxKind.MinusToken || !ts.isNumericLiteral(node.operand)) {
return throwError("The passed in PrefixUnaryExpression must be for a negative numeric literal.");
}
const result = parseFloat(node.operand.text);
if (isNaN(result)) {
return throwError(`Unable to parse negative numeric literal: ${node.operand.text}`);
}
return result * -1;
}
export function getReturnStatementExpressionFromBlock(block: ts.Block) {
for (const statement of block.statements) {
if (ts.isReturnStatement(statement) && statement.expression != null) {
return statement.expression;
}
}
return undefined;
}
// todo: remove the use of the printer except for exceptions
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
export function getNodeText(node: ts.Node, sourceFile: ts.SourceFile) {
return printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
}
================================================
FILE: packages/transforms-ts/src/index.ts
================================================
export { TransformResult } from "./transform";
export * from "./transformerFactory";
export { VisitSourceFileContext } from "./VisitSourceFileContext";
================================================
FILE: packages/transforms-ts/src/parse.ts
================================================
import { assertNever, throwError } from "@ts-nameof/common";
import * as common from "@ts-nameof/transforms-common";
import { createInterpolateNode, InterpolateNode } from "@ts-nameof/transforms-common";
import * as ts from "typescript";
import { getNegativeNumericLiteralValue, getNodeText, getReturnStatementExpressionFromBlock, isNegativeNumericLiteral } from "./helpers";
import { VisitSourceFileContext } from "./VisitSourceFileContext";
/**
* Parses a TypeScript AST node to a common NameofCallExpression or returns undefined if the current node
* is not a nameof call expression.
* @param parsingNode - Babel AST node to parse.
* @param sourceFile - Containing source file.
* @param context - Context for when visiting all the source file nodes
*/
export function parse(parsingNode: ts.Node, sourceFile: ts.SourceFile, context: VisitSourceFileContext | undefined) {
if (!isNameof(parsingNode)) {
return undefined;
}
const propertyName = parsePropertyName(parsingNode);
// Ignore nameof.interpolate function calls... they will be dealt with later.
if (isInterpolatePropertyName(propertyName)) {
handleNameofInterpolate(parsingNode);
return undefined;
}
return parseNameof(parsingNode);
function parseNameof(callExpr: ts.CallExpression): common.NameofCallExpression {
return {
property: propertyName,
typeArguments: parseTypeArguments(callExpr),
arguments: parseArguments(callExpr),
};
}
function parsePropertyName(callExpr: ts.CallExpression) {
const { expression } = callExpr;
if (!ts.isPropertyAccessExpression(expression) || !ts.isIdentifier(expression.name)) {
return undefined;
}
return expression.name.text;
}
function parseTypeArguments(callExpr: ts.CallExpression) {
if (callExpr.typeArguments == null) {
return [];
}
return callExpr.typeArguments.map(arg => parseCommonNode(arg));
}
function parseArguments(callExpr: ts.CallExpression) {
return callExpr.arguments.map(arg => parseCommonNode(arg));
}
function parseCommonNode(node: ts.Expression | ts.TypeNode | ts.EntityName): common.Node {
if (ts.isPropertyAccessExpression(node)) {
return parsePropertyAccessExpression(node);
}
if (ts.isElementAccessExpression(node)) {
return parseElementAccessExpression(node);
}
if (ts.isArrowFunction(node)) {
return parseFunctionReturnExpression(node, getArrowFunctionReturnExpression(node));
}
if (ts.isFunctionExpression(node)) {
return parseFunctionReturnExpression(node, getReturnStatementExpressionFromBlockOrThrow(node.body));
}
if (ts.isNonNullExpression(node) || ts.isParenthesizedExpression(node) || ts.isAsExpression(node)) {
return parseCommonNode(node.expression);
}
if (ts.isQualifiedName(node)) {
return parseQualifiedName(node);
}
if (ts.isTypeReferenceNode(node)) {
return parseCommonNode(node.typeName);
}
if (ts.isSpreadElement(node)) {
return parseCommonNode(node.expression);
}
if (ts.isNumericLiteral(node) || isNegativeNumericLiteral(node)) {
return parseNumeric(node);
}
if (ts.isStringLiteral(node)) {
return parseStringLiteral(node);
}
if (ts.isArrayLiteralExpression(node)) {
return parseArrayLiteralExpression(node);
}
if (ts.isIdentifier(node)) {
return parseIdentifier(node);
}
if (ts.isImportTypeNode(node)) {
return parseImportType(node);
}
if (ts.isLiteralTypeNode(node)) {
return parseCommonNode(node.literal); // skip over and go straight to the literal
}
if (node.kind === ts.SyntaxKind.ThisKeyword) {
return common.createIdentifierNode("this");
}
if (node.kind === ts.SyntaxKind.SuperKeyword) {
return common.createIdentifierNode("super");
}
if (ts.isNoSubstitutionTemplateLiteral(node)) {
return common.createTemplateExpressionNode([node.text]);
}
if (ts.isTemplateExpression(node)) {
return parseTemplateExpression(node);
}
if (isNameof(node) && isInterpolatePropertyName(parsePropertyName(node))) {
return parseInterpolateNode(node);
}
return throwError(
`Unhandled node kind (${node.kind}) in text: ${getNodeText(node, sourceFile)}`
+ ` (Please open an issue if you believe this should be supported.)`,
);
}
function parseArrayLiteralExpression(node: ts.ArrayLiteralExpression) {
const elements = node.elements.map(element => parseCommonNode(element));
return common.createArrayLiteralNode(elements);
}
function parsePropertyAccessExpression(node: ts.PropertyAccessExpression) {
const expressionCommonNode = parseCommonNode(node.expression);
const nameCommonNode = parseIdentifier(node.name);
getEndCommonNode(expressionCommonNode).next = nameCommonNode;
return expressionCommonNode;
}
function parseElementAccessExpression(node: ts.ElementAccessExpression) {
const expressionCommonNode = parseCommonNode(node.expression);
const argumentExpressionCommonNode = parseCommonNode(node.argumentExpression);
const computedCommonNode = common.createComputedNode(argumentExpressionCommonNode);
getEndCommonNode(expressionCommonNode).next = computedCommonNode;
return expressionCommonNode;
}
function parseQualifiedName(node: ts.QualifiedName) {
const leftCommonNode = parseCommonNode(node.left);
const rightCommonNode = parseCommonNode(node.right);
getEndCommonNode(leftCommonNode).next = rightCommonNode;
return leftCommonNode;
}
function parseNumeric(node: ts.NumericLiteral | ts.PrefixUnaryExpression) {
return common.createNumericLiteralNode(getNodeValue());
function getNodeValue() {
if (ts.isNumericLiteral(node)) {
return parseFloat(node.text);
}
return getNegativeNumericLiteralValue(node);
}
}
function parseStringLiteral(node: ts.StringLiteral) {
return common.createStringLiteralNode(node.text);
}
function parseIdentifier(node: ts.Node) {
const text = getIdentifierTextOrThrow(node);
return common.createIdentifierNode(text);
}
function parseFunctionReturnExpression(functionLikeNode: ts.FunctionLike, node: ts.Expression) {
const parameterNames = functionLikeNode.parameters.map(p => {
const name = p.name;
if (ts.isIdentifier(name)) {
return name.text;
}
return getNodeText(name, sourceFile);
});
return common.createFunctionNode(parseCommonNode(node), parameterNames);
}
function parseImportType(node: ts.ImportTypeNode) {
const importType = common.createImportTypeNode(node.isTypeOf || false, node.argument && parseCommonNode(node.argument));
const qualifier = node.qualifier && parseCommonNode(node.qualifier);
getEndCommonNode(importType).next = qualifier;
return importType;
}
function parseTemplateExpression(node: ts.TemplateExpression) {
return common.createTemplateExpressionNode(getParts());
function getParts() {
const parts: (string | InterpolateNode)[] = [];
if (node.head.text.length > 0) {
parts.push(node.head.text);
}
for (const templateSpan of node.templateSpans) {
parts.push(createInterpolateNode(templateSpan.expression, getNodeText(templateSpan.expression, sourceFile)));
parts.push(templateSpan.literal.text);
}
return parts;
}
}
function parseInterpolateNode(node: ts.CallExpression) {
if (node.arguments.length !== 1) {
return throwError(`Should never happen as this would have been tested for earlier.`);
}
return common.createInterpolateNode(node.arguments[0], getNodeText(node.arguments[0], sourceFile));
}
function getEndCommonNode(commonNode: common.Node) {
while (commonNode.next != null) {
commonNode = commonNode.next;
}
return commonNode;
}
function getArrowFunctionReturnExpression(func: ts.ArrowFunction) {
if (ts.isBlock(func.body)) {
return getReturnStatementExpressionFromBlockOrThrow(func.body);
}
return func.body;
}
function getIdentifierTextOrThrow(node: ts.Node) {
if (!ts.isIdentifier(node)) {
return throwError(`Expected node to be an identifier: ${getNodeText(node, sourceFile)}`);
}
return node.text;
}
function getReturnStatementExpressionFromBlockOrThrow(block: ts.Block) {
return getReturnStatementExpressionFromBlock(block)
|| throwError(`Cound not find return statement with an expression in function expression: ${getNodeText(block, sourceFile)}`);
}
function handleNameofInterpolate(callExpr: ts.CallExpression) {
if (callExpr.arguments.length !== 1) {
return throwError("Unexpected scenario where a nameof.interpolate function did not have a single argument.");
}
// Add the interpolate expression to the context so that it can be checked later to find
// nameof.interpolate calls that were never resolved.
if (context != null) {
context.interpolateExpressions.add(callExpr.arguments[0]);
}
}
function isNameof(node: ts.Node): node is ts.CallExpression {
if (!ts.isCallExpression(node)) {
return false;
}
const identifier = getIdentifierToInspect(node.expression);
return identifier != null && identifier.text === "nameof";
function getIdentifierToInspect(expression: ts.LeftHandSideExpression) {
if (ts.isIdentifier(expression)) {
return expression;
}
if (ts.isPropertyAccessExpression(expression) && ts.isIdentifier(expression.expression)) {
return expression.expression;
}
}
}
function isInterpolatePropertyName(propertyName: string | undefined) {
return propertyName === "interpolate";
}
}
================================================
FILE: packages/transforms-ts/src/tests/transformerFactoryTests.ts
================================================
import { runCommonTests } from "@ts-nameof/tests-common";
import * as ts from "typescript";
import { transformerFactory } from "../transformerFactory";
runCommonTests(run);
function run(text: string) {
const results: { fileName: string; fileText: string }[] = [];
const compilerOptions: ts.CompilerOptions = {
strictNullChecks: true,
target: ts.ScriptTarget.ES2017,
};
const transformers: ts.CustomTransformers = {
before: [transformerFactory],
after: [],
};
const testFileName = "/file.ts";
const host: ts.CompilerHost = {
fileExists: (fileName: string) => fileName === testFileName,
readFile: (fileName: string) => fileName === testFileName ? text : undefined,
getSourceFile: (fileName, languageVersion) => {
if (fileName !== testFileName) {
return undefined;
}
return ts.createSourceFile(fileName, text, languageVersion, false, ts.ScriptKind.TS);
},
getDefaultLibFileName: options => ts.getDefaultLibFileName(options),
writeFile: () => {
throw new Error("Not implemented");
},
getCurrentDirectory: () => "/",
getDirectories: () => [],
getCanonicalFileName: fileName => fileName,
useCaseSensitiveFileNames: () => true,
getNewLine: () => "\n",
};
const program = ts.createProgram(["/file.ts"], compilerOptions, host);
program.emit(undefined, (fileName, fileText) => results.push({ fileName, fileText }), undefined, false, transformers);
return results[0].fileText;
}
================================================
FILE: packages/transforms-ts/src/transform.ts
================================================
import { throwError } from "@ts-nameof/common";
import * as common from "@ts-nameof/transforms-common";
import * as ts from "typescript";
import { VisitSourceFileContext } from "./VisitSourceFileContext";
/**
* Resulting node type of a nameof transform.
*/
export type TransformResult = ts.StringLiteral | ts.ArrayLiteralExpression | ts.NoSubstitutionTemplateLiteral | ts.TemplateExpression;
/**
* Transforms a common node to a TypeScript compiler node.
* @param node Common node to be transformed.
*/
export function transform(node: common.Node, context: VisitSourceFileContext | undefined): TransformResult {
switch (node.kind) {
case "StringLiteral":
return ts.createLiteral(node.value);
case "ArrayLiteral":
return ts.createArrayLiteral(node.elements.map(element => transform(element, context)));
case "TemplateExpression":
if (node.parts.length === 1 && typeof node.parts[0] === "string") {
return ts.createNoSubstitutionTemplateLiteral(node.parts[0] as string);
}
return createTemplateExpression(node, context);
default:
return throwError(`Unsupported node kind: ${node.kind}`);
}
}
function createTemplateExpression(node: common.TemplateExpressionNode, context: VisitSourceFileContext | undefined) {
const firstPart = typeof node.parts[0] === "string" ? node.parts[0] as string : undefined;
const parts = firstPart != null ? node.parts.slice(1) : [...node.parts];
return ts.createTemplateExpression(ts.createTemplateHead(firstPart || ""), getParts());
function getParts() {
const templateSpans: ts.TemplateSpan[] = [];
for (let i = 0; i < parts.length; i += 2) {
const isLast = i + 2 === parts.length;
const interpolatedNode = parts[i];
if (typeof interpolatedNode === "string") {
return throwError("Unexpected scenario where an interpolated node was expected, but a string was found.");
}
const text = parts[i + 1];
if (typeof text !== "string") {
return throwError("Unexpected scenario where a string was expected, but an interpolated node was found.");
}
const tsExpression = interpolatedNode.expression as ts.Expression;
const tsText = !isLast ? ts.createTemplateMiddle(text) : ts.createTemplateTail(text);
// mark this nameof.interpolate expression as being handled
if (context != null) {
context.interpolateExpressions.delete(tsExpression);
}
templateSpans.push(ts.createTemplateSpan(tsExpression, tsText));
}
return templateSpans;
}
}
================================================
FILE: packages/transforms-ts/src/transformerFactory.ts
================================================
import { throwError, throwErrorForSourceFile } from "@ts-nameof/common";
import { transformCallExpression } from "@ts-nameof/transforms-common";
import * as ts from "typescript";
import { getNodeText } from "./helpers";
import { parse } from "./parse";
import { transform, TransformResult } from "./transform";
import { VisitSourceFileContext } from "./VisitSourceFileContext";
/** Transformer factory for performing nameof transformations. */
export const transformerFactory: ts.TransformerFactory<ts.SourceFile> = context => {
return file => visitSourceFile(file, context) as ts.SourceFile;
};
/** Visits all the nodes of the source file. */
export function visitSourceFile(sourceFile: ts.SourceFile, context: ts.TransformationContext) {
const visitSourceFileContext: VisitSourceFileContext = {
interpolateExpressions: new Set(),
};
try {
const result = visitNodeAndChildren(sourceFile);
throwIfContextHasInterpolateExpressions(visitSourceFileContext, sourceFile);
return result;
} catch (err: any) {
return throwErrorForSourceFile(err.message, sourceFile.fileName);
}
function visitNodeAndChildren(node: ts.Node): ts.Node {
if (node == null) {
return node;
}
// visit the children in post order
node = ts.visitEachChild(node, childNode => visitNodeAndChildren(childNode), context);
return visitNode(node, sourceFile, visitSourceFileContext);
}
}
/**
* Throws if the context contains any remaining interpolate expressions.
* @param context - Context to check.
* @param sourceFile - Source file being transformed.
*/
export function throwIfContextHasInterpolateExpressions(context: VisitSourceFileContext, sourceFile: ts.SourceFile) {
if (context.interpolateExpressions.size > 0) {
const firstResult = Array.from(context.interpolateExpressions.values())[0];
return throwError(
`Found a nameof.interpolate that did not exist within a `
+ `nameof.full call expression: nameof.interpolate(${getNodeText(firstResult, sourceFile)})`,
);
}
}
/** Visit a node and do a nameof transformation on it if necessary. */
export function visitNode(visitingNode: ts.Node, sourceFile: ts.SourceFile): TransformResult;
/** @internal */
export function visitNode(visitingNode: ts.Node, sourceFile: ts.SourceFile, context: VisitSourceFileContext | undefined): TransformResult;
export function visitNode(visitingNode: ts.Node, sourceFile: ts.SourceFile, context?: VisitSourceFileContext) {
const parseResult = parse(visitingNode, sourceFile, context);
if (parseResult == null) {
return visitingNode;
}
return transform(transformCallExpression(parseResult), context);
}
================================================
FILE: packages/transforms-ts/tsconfig.json
================================================
{
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "./dist"
},
"extends": "../../tsconfig.common.json",
"include": ["./src/**/*.ts"],
"references": [
{ "path": "../common" },
{ "path": "../transforms-common" },
{ "path": "../tests-common" }
]
}
================================================
FILE: packages/ts-nameof/.mocharc.yml
================================================
require: ts-node/register
recursive: true
reporter: progress
watch-extensions: ts
timeout: 10000
spec: src/tests/**/*.ts
================================================
FILE: packages/ts-nameof/.npmignore
================================================
/node_modules
/.vscode
/.vs
/.git
/obj
/temp
/bin
/src
/dist/tests
/setup
/scripts
/lib
*.js.map
*.v12.suo
*.csproj
*.csproj.user
*.sln
*.log
.travis.yml
.gitignore
CHANGELOG.md
.mocharc.yml
tsconfig.json
tsconfig.scripts.json
tsconfig.tsbuildinfo
================================================
FILE: packages/ts-nameof/LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2019 David Sherret
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: packages/ts-nameof/README.md
================================================
# ts-nameof
[](https://badge.fury.io/js/ts-nameof)
[](https://travis-ci.org/dsherret/ts-nameof)
[](http://github.com/badges/stability-badges)
[`nameof`](https://msdn.microsoft.com/en-us/library/dn986596.aspx) in TypeScript.
```
npm install ts-nameof @types/ts-nameof --save-dev
```
## Setup
### 1. Build Setup
Follow any of these instructions:
- [Webpack](https://github.com/dsherret/ts-nameof/blob/master/packages/ts-nameof/setup/webpack.md)
- [Gulp](https://github.com/dsherret/ts-nameof/blob/master/packages/ts-nameof/setup/gulp.md)
- [FuseBox](https://github.com/dsherret/ts-nameof/blob/master/packages/ts-nameof/setup/fusebox.md)
- [tsc](https://github.com/dsherret/ts-nameof/blob/master/packages/ts-nameof/setup/tsc.md)
- [Jest](https://github.com/dsherret/ts-nameof/blob/master/packages/ts-nameof/setup/jest.md)
- [Custom](https://github.com/dsherret/ts-nameof/blob/master/packages/ts-nameof/setup/custom.md)
- Others - Open an [issue](https://github.com/dsherret/ts-nameof/issues).
These instructions need updating to use the transformation API, but will still work in the meantime:
- Nuxt - Use [https://github.com/Kukks/nuxt-ts-nameof](https://github.com/Kukks/nuxt-ts-nameof#readme)
### 2. Declaring global `nameof` function
Install `@types/ts-nameof`:
```
npm install @types/ts-nameof --save-dev
```
## Transforms
[Read here](https://github.com/dsherret/ts-nameof/blob/master/README.md)
## Other
- [Contributing](https://github.com/dsherret/ts-nameof/blob/master/CONTRIBUTING.md)
- [Development](https://github.com/dsherret/ts-nameof/blob/master/DEVELOPMENT.md)
================================================
FILE: packages/ts-nameof/lib/declarationFileTests.ts
================================================
/// <reference path="../ts-nameof.d.ts" />
/* istanbul ignore next */
import tsNameOf = require("ts-nameof");
import { assert, IsExact } from "conditional-type-checks";
import * as tsNameOfEs6 from "ts-nameof";
/* istanbul ignore next */
function testFunc() {
tsNameOf.replaceInFiles(["test"]);
tsNameOf.replaceInFiles(["test"]).then(() => {});
// replaceInText
const replaceInTextResult = tsNameOf.replaceInText("fileName.ts", "const t = 5;");
console.log(replaceInTextResult);
assert<IsExact<typeof replaceInTextResult.fileText, string | undefined>>(true);
assert<IsExact<typeof replaceInTextResult.replaced, boolean>>(true);
// es6 test
const es6Result = tsNameOfEs6.replaceInText("file.ts", "");
console.log(es6Result.replaced);
}
================================================
FILE: packages/ts-nameof/package.json
================================================
{
"name": "ts-nameof",
"version": "5.0.0",
"description": "nameof in TypeScript",
"main": "dist/main.js",
"typings": "ts-nameof.d.ts",
"scripts": {
"clean": "rimraf dist && tsc --b --clean",
"build": "tsc --b && npm run build:declarations",
"build-test": "tsc --build && npm run --silent copy-test-files",
"test": "npm run build-test && mocha",
"test:debug": "npm run build-test && mocha --inspect-brk",
"copy-test-files": "rimraf temp && copyfiles -u 2 \"./src/tests/testFiles/**/*{.js,.txt}\" \"./temp\"",
"build:declarations": "ts-node --project scripts/tsconfig.json scripts/generation/main create-declaration-file && npm run --silent verify-declaration-file",
"verify-declaration-file": "ts-node --project scripts/tsconfig.json scripts/verification/main verify-declaration-file",
"dopublish": "npm run test && npm run build && npm run verify-declaration-file && echo \"run: npm publish --otp\""
},
"repository": {
"type": "git",
"url": "git+https://github.com/dsherret/ts-nameof.git",
"directory": "packages/ts-nameof"
},
"keywords": [
"nameof",
"typescript",
"transformer",
"custom-transformer"
],
"author": "David Sherret",
"license": "MIT",
"bugs": {
"url": "https://github.com/dsherret/ts-nameof/issues"
},
"homepage": "https://github.com/dsherret/ts-nameof#readme",
"peerDependencies": {
"typescript": "*"
},
"dependencies": {
"@ts-nameof/common": "^4.2.1",
"@ts-nameof/transforms-ts": "^4.2.1",
"glob": "^7.2.0"
},
"devDependencies": {
"@ts-nameof/scripts-common": "^4.0.2",
"@ts-nameof/tests-common": "^4.2.0",
"@types/glob": "^7.2.0",
"@types/mocha": "^9.0.0",
"@types/node": "^17.0.0",
"conditional-type-checks": "^1.0.5",
"copyfiles": "^2.4.1",
"mocha": "^9.1.3",
"rimraf": "^3.0.2",
"ts-morph": "^13.0.2",
"ts-node": "^10.4.0",
"typescript": "^4.4.3"
}
}
================================================
FILE: packages/ts-nameof/scripts/common/createProject.ts
================================================
import { Project } from "ts-morph";
export function getProject() {
return new Project({ tsConfigFilePath: "tsconfig.json", compilerOptions: { declaration: true } });
}
================================================
FILE: packages/ts-nameof/scripts/common/index.ts
================================================
export * from "./createProject";
================================================
FILE: packages/ts-nameof/scripts/generation/createDeclarationFile.ts
================================================
import { ModuleDeclarationKind, Project } from "ts-morph";
export function createDeclarationFile(project: Project) {
const mainFile = project.getSourceFileOrThrow("src/main.ts");
const outputFiles = mainFile.getEmitOutput({ emitOnlyDtsFiles: true }).getOutputFiles();
if (outputFiles.length !== 1) {
throw new Error(`Expected 1 file when emitting, but had ${outputFiles.length}`);
}
const declarationFile = project.createSourceFile("ts-nameof.d.ts", outputFiles[0].getText(), { overwrite: true });
removePreceedingCommentReference();
commentExternalTypes();
removeTypeScriptImport();
wrapInGlobalModule();
addGlobalDeclarations();
function removePreceedingCommentReference() {
const firstChild = declarationFile.getFirstChildOrThrow();
declarationFile.removeText(0, firstChild.getStart());
}
function commentExternalTypes() {
// these types are made to be any so that this library will work when included in
// web projects and NodeJS does not exist. See issue #22.
const typesToComment = [
"ts.TransformerFactory<ts.SourceFile>",
"NodeJS.ErrnoException",
];
declarationFile.forEachDescendant(descendant => {
if (typesToComment.indexOf(descendant.getText()) >= 0) {
descendant.replaceWithText(`any /* ${descendant.getText()} */`);
}
});
}
function removeTypeScriptImport() {
declarationFile.getImportDeclarationOrThrow("typescript").remove();
}
function wrapInGlobalModule() {
const fileText = declarationFile.getText();
declarationFile.removeText();
const apiModule = declarationFile.addModule({
hasDeclareKeyword: true,
declarationKind: ModuleDeclarationKind.Module,
name: `"ts-nameof"`,
});
apiModule.setBodyText(fileText);
apiModule.getVariableStatementOrThrow(s => s.getDeclarations().some(d => d.getName() === "api"))
.setHasDeclareKeyword(false);
}
function addGlobalDeclarations() {
const globalFile = project.addSourceFileAtPath("../../lib/global.d.ts");
declarationFile.addStatements(writer => {
writer.newLine();
writer.write(globalFile.getText().replace(/\r?\n$/, ""));
});
}
}
================================================
FILE: packages/ts-nameof/scripts/generation/main.ts
================================================
import { ArgsChecker } from "@ts-nameof/scripts-common";
import { getProject } from "../common";
import { createDeclarationFile } from "./createDeclarationFile";
const argsChecker = new ArgsChecker();
const project = getProject();
if (argsChecker.checkHasArg("create-declaration-file")) {
console.log("Creating declaration file...");
createDeclarationFile(project);
}
argsChecker.verifyArgsUsed();
project.save();
================================================
FILE: packages/ts-nameof/scripts/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es6",
"noEmit": true
},
"extends": "../../../tsconfig.common.json",
"exclude": [
"./lib",
"./src"
]
}
================================================
FILE: packages/ts-nameof/scripts/verification/main.ts
================================================
import { ArgsChecker } from "@ts-nameof/scripts-common";
import { verifyDeclarationFile } from "./verifyDeclarationFile";
const argsChecker = new ArgsChecker();
if (argsChecker.checkHasArg("verify-declaration-file")) {
console.log("Verifying declaration file...");
verifyDeclarationFile();
}
argsChecker.verifyArgsUsed();
================================================
FILE: packages/ts-nameof/scripts/verification/verifyDeclarationFile.ts
================================================
import { Project } from "ts-morph";
export function verifyDeclarationFile() {
const project = new Project();
const declarationFile = project.addSourceFileAtPath("ts-nameof.d.ts");
const declarationFileTests = project.addSourceFileAtPath("lib/declarationFileTests.ts");
const diagnostics = [...declarationFile.getPreEmitDiagnostics(), ...declarationFileTests.getPreEmitDiagnostics()];
if (diagnostics.length > 0) {
console.error(project.formatDiagnosticsWithColorAndContext(diagnostics));
}
}
================================================
FILE: packages/ts-nameof/setup/custom.md
================================================
# Using ts-nameof with a Custom Setup
## Transformation API
The export from `ts-nameof` is a `ts.TransformerFactory<ts.SourceFile>` so that can be used anywhere custom transformers are accepted. For example, see the [webpack instructions](webpack.md).
```ts
const tsNameof = require("ts-nameof");
// tsNameof is a ts.TransformerFactory<ts.SourceFile>
```
If you find this works for the library you're using to do a build then please consider submitting a PR with setup instructions for that library.
## Working with text
Note that these solution require reparsing the source file text and that might make the build slow. The other solutions mostly all use the
transformation api which will be much faster.
### Replacing in Files
You can use `replaceInFiles` to replace in .ts files:
```javascript
var replaceInFiles = require("ts-nameof").replaceInFiles;
replaceInFiles(["./dist/**/*.ts"]);
```
1. Copy your .ts files to a build folder. This is necessary so you don't overwrite your original source files.
2. Run `replaceInFiles` on these files.
3. Compile the final typescript files.
### Replacing in Text
You can also use the `replaceInText` function to replace occurrences of `nameof` in any string:
```javascript
var replaceInText = require("ts-nameof").replaceInText;
var replacedText = replaceInText("filename.ts", "nameof(test);");
```
================================================
FILE: packages/ts-nameof/setup/fusebox.md
================================================
# Using ts-nameof with FuseBox
To use ts-nameof with [FuseBox](https://github.com/fuse-box/fuse-box), specify it as a custom transformer:
```javascript
const tsNameof = require("ts-nameof");
FuseBox.init({
transformers: {
before: [tsNameof],
},
});
```
================================================
FILE: packages/ts-nameof/setup/gulp.md
================================================
# Using ts-nameof with Gulp
Specify it as a custom transformer with [gulp-typescript](https://github.com/ivogabe/gulp-typescript):
```javascript
const gulp = require("gulp");
const ts = require("gulp-typescript");
const tsNameof = require("ts-nameof");
gulp.task("typescript", function() {
gulp.src("src/**/*.ts")
.pipe(ts({
getCustomTransformers: () => ({ before: [tsNameof] }),
}))
.pipe(gulp.dest("dist"));
});
```
================================================
FILE: packages/ts-nameof/setup/jest.md
================================================
# Using ts-nameof with Jest
1. Setup jest with [ts-jest](https://github.com/kulshekhar/ts-jest)
2. `npm install --save-dev ts-nameof @types/ts-nameof`
3. In _package.json_ specify...
```jsonc
{
// ...
"jest": {
"globals": {
"ts-jest": {
"astTransformers": ["ts-nameof"]
}
}
}
}
```
...or in _jest.config.js_...
```ts
module.exports = {
// ...
globals: {
"ts-jest": {
"astTransformers": ["ts-nameof"],
},
},
};
```
================================================
FILE: packages/ts-nameof/setup/tsc.md
================================================
# Using ts-nameof with tsc
Transformation plugins are currently not supported by `tsc` alone. Please go and upvote [this issue](https://github.com/Microsoft/TypeScript/issues/14419) on TypeScript's issue tracker.
In the meantime, this is possible using [ttypescript](https://github.com/cevek/ttypescript) thanks to [@cevek](https://github.com/cevek)!
## Setup
1. Install `ttypescript` and `ts-nameof`:
```bash
npm install --save-dev ttypescript ts-nameof
// or
yarn add --dev ttypescript ts-nameof
```
2. Add `ts-nameof` to `tsconfig.json` as a custom transformer:
```json
{
"compilerOptions": {
"plugins": [{ "transform": "ts-nameof", "type": "raw" }]
}
}
```
3. Compile with `ttsc` instead of `tsc`:
```bash
npx ttsc
```
### Swapping out TypeScript with TTypeScript
Read the instructions at [`ttypescript`'s GitHub page](https://github.com/cevek/ttypescript) for how to use with tools like `ts-node` and visual studio code.
Generally, most build tools have a way to swap out which version of the compiler you use (ex. using environment variables or command line arguments) so this should work across a lot of build tools.
================================================
FILE: packages/ts-nameof/setup/webpack.md
================================================
# Using ts-nameof with Webpack
## ts-loader / awesome-typescript-loader
If using [ts-loader](https://github.com/TypeStrong/ts-loader) or [awesome-typescript-loader](https://github.com/s-panferov/awesome-typescript-loader), specify ts-nameof as a custom transformation like so in _webpack.config.js_:
```ts
const tsNameof = require("ts-nameof");
module.exports = {
// ...etc...
module: {
rules: [{
test: /\.tsx?$/,
use: [{
loader: "ts-loader", // or awesome-typescript-loader
options: {
getCustomTransformers: () => ({ before: [tsNameof] }),
},
}],
}],
},
};
```
================================================
FILE: packages/ts-nameof/src/main.ts
================================================
import { transformerFactory } from "@ts-nameof/transforms-ts";
import * as ts from "typescript";
import { replaceInFiles, replaceInText } from "./text";
interface Api {
(): ts.TransformerFactory<ts.SourceFile>;
replaceInFiles(fileNames: ReadonlyArray<string>): Promise<void[]>;
replaceInText(fileName: string, fileText: string): { fileText?: string; replaced: boolean };
}
const api: Api = transformerFactory as any as Api;
api.replaceInFiles = replaceInFiles;
api.replaceInText = replaceInText;
// this is for ts-jest support... not ideal
(api as any).factory = () => transformerFactory;
export = api;
================================================
FILE: packages/ts-nameof/src/tests/testFiles/GeneralTestFile.txt
================================================
console.log(nameof(alert));
console.log(nameof(window.alert));
console.log(nameof.full(window.alert));
console.log(nameof.full(window .alert));
console.log(nameof.full(window.alert.length, -3));
console.log(nameof.full(window.alert.length, -2));
console.log(nameof.full(window.alert.length, -1));
console.log(nameof.full(window.alert.length, 0));
console.log(nameof.full(window.alert.length, 1));
console.log(nameof.full(window.alert.length, 2));
console.log( nameof( window ) );
console.log( nameof (window) );
console.log(nameof(nameof(nameof(clearTimeout))));
console.log(nameof<Array<string>>());
console.log(nameof.full<Array<string>>());
console.log(nameof<MyInterface>(i => i.prop));
console.log(nameof<MyInterface>(function(i) { return i.prop; }));
console.log(nameof<MyInterface>(function(i) { return i.prop }));
console.log(nameof.full<MyNamespace.MyInnerInterface>());
console.log(nameof.full<MyNamespace.MyInnerInterface>(-2));
console.log(nameof.full<MyNamespace.MyInnerInterface>(-1));
console.log(nameof.full<MyNamespace.MyInnerInterface>(0));
console.log(nameof.full<MyNamespace.MyInnerInterface>(1));
================================================
FILE: packages/ts-nameof/src/tests/testFiles/StreamNoNameofTestFile.txt
================================================
console.log("");
================================================
FILE: packages/ts-nameof/src/tests/testFiles/StreamTestFile.txt
================================================
nameof(window);
================================================
FILE: packages/ts-nameof/src/tests/testFiles/globFolder/MyGlobTestFile.txt
================================================
console.log(nameof(console));
================================================
FILE: packages/ts-nameof/src/tests/testFiles/issues/11-expected.txt
================================================
class Test {
private x: Test;
public y: Test;
public z: string;
constructor() {
"x";
"y";
"this.x.y.z";
"x.y.z";
}
}
================================================
FILE: packages/ts-nameof/src/tests/testFiles/issues/11-source.txt
================================================
class Test {
private x: Test;
public y: Test;
public z: string;
constructor() {
nameof(this.x);
nameof(this.x.y);
nameof.full(this.x.y.z);
nameof.full(this.x.y.z, 1);
}
}
================================================
FILE: packages/ts-nameof/src/tests/testFiles/issues/8-expected.txt
================================================
import CodeBlockWriter from "code-block-writer";
import {expect} from "chai";
import {FunctionDefinition, InterfaceMethodDefinition, ClassMethodDefinition} from "./../../definitions";
import {FunctionBodyWriter} from "./../../writers";
import {WriteFlags} from "./../../WriteFlags";
import * as mocks from "./mocks";
describe("FunctionBodyWriter", () => {
function createObjects() {
const writer = new CodeBlockWriter();
const defWriter = new FunctionBodyWriter(writer);
return {writer, defWriter};
}
describe("write", () => {
it(`should not write out the function body if ${"HideFunctionBodies"} is set`, () => {
const def = new FunctionDefinition();
const {writer, defWriter} = createObjects();
defWriter.write(def, WriteFlags.HideFunctionBodies);
expect(writer.toString()).to.equal(";");
});
it(`should not write out the function body if it's an ${"InterfaceMethodDefinition"}`, () => {
const def = new InterfaceMethodDefinition();
const {writer, defWriter} = createObjects();
defWriter.write(def, WriteFlags.None);
expect(writer.toString()).to.equal(";");
});
});
});
================================================
FILE: packages/ts-nameof/src/tests/testFiles/issues/8-source.txt
================================================
import CodeBlockWriter from "code-block-writer";
import {expect} from "chai";
import {FunctionDefinition, InterfaceMethodDefinition, ClassMethodDefinition} from "./../../definitions";
import {FunctionBodyWriter} from "./../../writers";
import {WriteFlags} from "./../../WriteFlags";
import * as mocks from "./mocks";
describe(nameof(FunctionBodyWriter), () => {
function createObjects() {
const writer = new CodeBlockWriter();
const defWriter = new FunctionBodyWriter(writer);
return {writer, defWriter};
}
describe(nameof<FunctionBodyWriter>(w => w.write), () => {
it(`should not write out the function body if ${nameof(WriteFlags.HideFunctionBodies)} is set`, () => {
const def = new FunctionDefinition();
const {writer, defWriter} = createObjects();
defWriter.write(def, WriteFlags.HideFunctionBodies);
expect(writer.toString()).to.equal(";");
});
it(`should not write out the function body if it's an ${nameof(InterfaceMethodDefinition)}`, () => {
const def = new InterfaceMethodDefinition();
const {writer, defWriter} = createObjects();
defWriter.write(def, WriteFlags.None);
expect(writer.toString()).to.equal(";");
});
});
});
================================================
FILE: packages/ts-nameof/src/tests/text/helpers/fileHelpers.ts
================================================
import * as fs from "fs";
export function readFile(path: string) {
return new Promise<string>((resolve, reject) => {
fs.readFile(path, { encoding: "utf-8" }, (err, data) => {
if (err) {
reject(err);
return;
}
resolve(data);
});
});
}
export function writeFile(path: string, contents: string) {
return new Promise<void>((resolve, reject) => {
fs.writeFile(path, contents, err => {
if (err) {
reject(err);
return;
}
resolve();
});
});
}
================================================
FILE: packages/ts-nameof/src/tests/text/helpers/getTestFilePath.ts
================================================
import * as path from "path";
export function getTestFilePath(...paths: string[]) {
return path.join("./temp/testFiles", ...paths);
}
================================================
FILE: packages/ts-nameof/src/tests/text/helpers/index.ts
================================================
export * from "./fileHelpers";
export * from "./getTestFilePath";
================================================
FILE: packages/ts-nameof/src/tests/text/issuesTests.ts
================================================
import * as assert from "assert";
import { replaceInFiles } from "../../text";
import { getTestFilePath, readFile, writeFile } from "./helpers";
describe("replaceInFiles()", () => {
async function runTest(fileName: string, expectedFileName: string) {
fileName = getTestFilePath(fileName);
expectedFileName = getTestFilePath(expectedFileName);
const originalFileText = await readFile(expectedFileName);
try {
await replaceInFiles([fileName]);
const data = await readFile(fileName);
const expectedContents = await readFile(expectedFileName);
assert.equal(data.replace(/\r?\n/g, "\n"), expectedContents.replace(/\r?\n/g, "\n"));
} finally {
await writeFile(expectedFileName, originalFileText);
}
}
function runIssueTest(issueNumber: number) {
describe(`issue ${issueNumber}`, () => {
it("should replace", async () => {
await runTest(`issues/${issueNumber}-source.txt`, `issues/${issueNumber}-expected.txt`);
});
});
}
runIssueTest(8);
runIssueTest(11);
});
================================================
FILE: packages/ts-nameof/src/tests/text/replaceInFilesTests.ts
================================================
import * as assert from "assert";
import { replaceInFiles } from "../../text";
import { getTestFilePath, readFile, writeFile } from "./helpers";
describe("replaceInFiles()", () => {
interface FileInfo {
filePath: string;
contents: string;
}
async function runTest(paths: string[], expectedFiles: FileInfo[]) {
paths = paths.map(p => getTestFilePath(p));
expectedFiles.forEach(f => f.filePath = getTestFilePath(f.filePath));
const initialFiles = await Promise.all(expectedFiles.map(f =>
readFile(f.filePath).then(data => ({
filePath: f.filePath,
contents: data,
} as FileInfo))
));
try {
await replaceInFiles(paths);
const readFilePromises = expectedFiles.map(f => readFile(f.filePath).then(data => ({ data, expectedContents: f.contents })));
for (const promise of readFilePromises) {
const { data, expectedContents } = await promise;
assert.equal(data.replace(/\r?\n/g, "\n"), expectedContents.replace(/\r?\n/g, "\n"));
}
} finally {
await Promise.all(initialFiles.map(f => writeFile(f.filePath, f.contents)));
}
}
describe("glob support", () => {
it("should replace in MyGlobTestFile.txt", async () => {
await runTest(["globFolder/**/*.txt"], [{
filePath: "globFolder/MyGlobTestFile.txt",
contents: `console.log("console");\n`,
}]);
});
});
describe("general file", () => {
it("should have the correct number of characters", async () => {
// because an IDE might auto-format the code, this makes sure that hasn't happened
assert.equal((await readFile(getTestFilePath("GeneralTestFile.txt"))).replace(/\r?\n/g, "\n").length, 1121);
});
const expected = `console.log("alert");
console.log("alert");
console.log("window.alert");
console.log("window.alert");
console.log("window.alert.length");
console.log("alert.length");
console.log("length");
console.log("window.alert.length");
console.log("alert.length");
console.log("length");
console.log( "window" );
console.log( "window" );
console.log("clearTimeout");
console.log("Array");
console.log("Array");
console.log("prop");
console.log("prop");
console.log("prop");
console.log("MyNamespace.MyInnerInterface");
console.log("MyNamespace.MyInnerInterface");
console.log("MyInnerInterface");
console.log("MyNamespace.MyInnerInterface");
console.log("MyInnerInterface");
`;
describe("file modifying test", () => {
it("should modify the file", async () => {
await runTest(["GeneralTestFile.txt"], [{
filePath: "GeneralTestFile.txt",
contents: expected,
}]);
});
});
});
});
================================================
FILE: packages/ts-nameof/src/tests/text/replaceInTextTests.ts
================================================
import { runCommonTests } from "@ts-nameof/tests-common";
import * as assert from "assert";
import { replaceInText } from "../../text";
describe("replaceInText", () => {
it("should unofficially maintain backwards compatibility when providing one argument", () => {
assert.equal((replaceInText as any)("nameof(window);").fileText, `"window";`);
});
it("should not replace when no nameof", () => {
const result = replaceInText("file.ts", "some random text with no nameof in it");
assert.equal(result.replaced, false);
assert.equal(result.fileText, undefined);
});
it("should replace when there was a nameof", () => {
const result = replaceInText("file.ts", "describe(nameof(myTest), () => {});");
assert.equal(result.replaced, true);
assert.equal(result.fileText, `describe("myTest", () => {});`);
});
it("should replace when there was a nameof in tsx file", () => {
const result = replaceInText("file.tsx", "const t = <div t={nameof(t)} />;");
assert.equal(result.replaced, true);
assert.equal(result.fileText, `const t = <div t={"t"} />;`);
});
runCommonTests(text => {
return replaceInText("file.ts", text).fileText || text;
});
});
================================================
FILE: packages/ts-nameof/src/text/getFileNamesFromGlobs.ts
================================================
import glob from "glob";
export function getFileNamesFromGlobs(globs: ReadonlyArray<string>) {
const promises = globs.map(g => getFileNamesFromGlob(g));
return Promise.all(promises).then(values => values.reduce((a, b) => a.concat(b), []));
}
function getFileNamesFromGlob(globFileName: string) {
return new Promise<string[]>((resolve, reject) => {
glob(globFileName, (err, files) => {
/* istanbul ignore if */
if (err) {
reject(err);
return;
}
resolve(files);
});
});
}
================================================
FILE: packages/ts-nameof/src/text/index.ts
================================================
export * from "./getFileNamesFromGlobs";
export * from "./replaceInFiles";
export * from "./replaceInText";
================================================
FILE: packages/ts-nameof/src/text/replaceInFiles.ts
================================================
import * as fs from "fs";
import { getFileNamesFromGlobs } from "./getFileNamesFromGlobs";
import { replaceInText } from "./replaceInText";
export function replaceInFiles(fileNames: ReadonlyArray<string>): Promise<void[]> {
return getFileNamesFromGlobs(fileNames).then(globbedFileNames => doReplaceInFiles(globbedFileNames));
}
function doReplaceInFiles(fileNames: ReadonlyArray<string>) {
const promises: Promise<void>[] = [];
fileNames.forEach(fileName => {
promises.push(
new Promise<void>((resolve, reject) => {
fs.readFile(fileName, { encoding: "utf8" }, (err, fileText) => {
/* istanbul ignore if */
if (err) {
reject(err);
return;
}
const result = replaceInText(fileName, fileText);
if (result.replaced) {
fs.writeFile(fileName, result.fileText!, writeErr => {
/* istanbul ignore if */
if (writeErr) {
reject(writeErr);
return;
}
resolve();
});
} else {
resolve();
}
});
}),
);
});
return Promise.all(promises);
}
================================================
FILE: packages/ts-nameof/src/text/replaceInText.ts
================================================
import { throwIfContextHasInterpolateExpressions, visitNode, VisitSourceFileContext } from "@ts-nameof/transforms-ts";
import * as ts from "typescript";
const printer = ts.createPrinter();
export function replaceInText(fileName: string, fileText: string): { fileText?: string; replaced: boolean } {
// unofficial pre-2.0 backwards compatibility for this method
if (arguments.length === 1) {
fileText = fileName;
fileName = "/file.tsx"; // assume tsx
}
const visitSourceFileContext: VisitSourceFileContext = {
interpolateExpressions: new Set<ts.Node>(),
};
const sourceFile = ts.createSourceFile(fileName, fileText, ts.ScriptTarget.Latest, false);
const transformations: { start: number; end: number; text: string }[] = [];
const transformerFactory: ts.TransformerFactory<ts.SourceFile> = context => {
// this will always use the source file above
return _ => visitSourceFile(context);
};
ts.transform(sourceFile, [transformerFactory]);
throwIfContextHasInterpolateExpressions(visitSourceFileContext, sourceFile);
if (transformations.length === 0) {
return { replaced: false };
}
return { fileText: getTransformedText(), replaced: true };
function getTransformedText() {
let finalText = "";
let lastPos = 0;
for (const transform of transformations) {
finalText += fileText.substring(lastPos, transform.start);
finalText += transform.text;
lastPos = transform.end;
}
finalText += fileText.substring(lastPos);
return finalText;
}
function visitSourceFile(context: ts.TransformationContext) {
return visitNodeAndChildren(sourceFile) as ts.SourceFile;
function visitNodeAndChildren(node: ts.Node): ts.Node {
if (node == null) {
return node;
}
node = ts.visitEachChild(node, childNode => visitNodeAndChildren(childNode), context);
const resultNode = visitNode(node, sourceFile, visitSourceFileContext);
const wasTransformed = resultNode !== node;
if (wasTransformed) {
storeTransformation();
}
return resultNode;
function storeTransformation() {
const nodeStart = node.getStart(sourceFile);
const lastTransformation = transformations[transformations.length - 1];
// remove the last transformation if it's nested within this transformation
if (lastTransformation != null && lastTransformation.start > nodeStart) {
transformations.pop();
}
transformations.push({
start: nodeStart,
end: node.end,
text: printer.printNode(ts.EmitHint.Unspecified, resultNode, sourceFile),
});
}
}
}
}
================================================
FILE: packages/ts-nameof/ts-nameof.d.ts
================================================
declare module "ts-nameof" {
interface Api {
(): any /* ts.TransformerFactory<ts.SourceFile> */;
replaceInFiles(fileNames: ReadonlyArray<string>): Promise<void[]>;
replaceInText(fileName: string, fileText: string): {
fileText?: string;
replaced: boolean;
};
}
const api: Api;
export = api;
}
declare function nameof<T>(func?: (obj: T) => any): string;
/**
* Gets a string representation of the last identifier of the given expression.
*
* @example nameof(console) -> "console"
* @example nameof(console.log) -> "log"
* @example nameof(console["warn"]) -> "warn"
*
* @param obj An expression for which the last identifier will be parsed.
*/
declare function nameof(obj: any): string;
declare namespace nameof {
/**
* Gets the string representation of the entire type parameter expression.
*
* @example nameof.full<MyNamespace.MyInnerInterface>() -> "MyNamespace.MyInnerInterface"
* @example nameof.full<MyNamespace.MyInnerInterface>(1) -> "MyInnerInterface"
* @example nameof.full<Array<MyInterface>>() -> "Array"
* @example nameof.full<MyNamespace.AnotherNamespace.MyInnerInterface>>(-1) -> "MyInnerInterface"
*
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function full<T>(periodIndex?: number): string;
/**
* Gets the string representation of the entire resultant expression.
*
* @example nameof.full<MyInterface>(o => o.prop.prop2) -> "prop.prop2"
* @example nameof.full<MyInterface>(o => o.prop.prop2.prop3, 1) -> "prop2.prop3"
* @example nameof.full<MyInterface>(o => o.prop.prop2.prop3, -1) -> `"prop3"
*
* @param func A function for which the result will be parsed, excluding the parameter's identifier.
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function full<T>(func: (obj: T) => any, periodIndex?: number): string;
/**
* Gets the string representation of the entire given expression.
*
* @example nameof.full(console.log) -> "console.log"
* @example nameof.full(window.alert.length, -1) -> "length"
* @example nameof.full(window.alert.length, 2) -> "length"
*
* @param obj The expression which will be parsed.
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function full(obj: any, periodIndex?: number): string;
/**
* Gets an array containing the string representation of the final identifier of each expression in the array returned by the provided function.
*
* @example nameof.toArray<MyType>(o => [o.firstProp, o.otherProp.secondProp, o.other]) -> ["firstProp", "secondProp", "other"]
* @example nameof.toArray<MyType>(o => [o.prop, nameof.full(o.myProp.otherProp, 1)]) -> ["prop", "myProp.otherProp"]
*
* @param func A function returning an array of expressions to be parsed, excluding the parameter's identifier.
*/
function toArray<T>(func: (obj: T) => any[]): string[];
/**
* Gets an array containing the string representation of each expression in the arguments.
*
* @example nameof.toArray(myObject, otherObject) -> ["myObject", "otherObject"]
* @example nameof.toArray(obj.firstProp, obj.secondProp, otherObject, nameof.full(obj.other)) -> ["firstProp", "secondProp", "otherObject", "obj.other"]
*
* @param args An array of expressions to be parsed.
*/
function toArray(...args: any[]): string[];
/**
* Embeds an expression into the string representation of the result of nameof.full.
*
* @example nameof.full(myObj.prop[nameof.interpolate(i)]) -> `myObj.prop[${i}]`
*
* @param value The value to interpolate.
*/
function interpolate<T>(value: T): T;
/**
* Gets an array of strings where each element is a subsequent part of the expression provided.
*
* @example nameof.split<MyInterface>(o => o.prop.prop2.prop3) -> ["prop", "prop2", "prop3"]
* @example nameof.split<MyInterface>(o => o.prop.prop2.prop3, 1) -> ["prop2", "prop3"]
* @example nameof.split<MyInterface>(o => o.prop.prop2.prop3, -1) -> ["prop", "prop2"]
*
* @param func A function for which the resultant parts will be parsed, excluding the parameter's identifier.
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function split<T>(func: (obj: T) => any, periodIndex?: number): string[];
/**
* Gets an array of strings where each element is a subsequent part of the expression provided.
*
* @example nameof.split(myObj.prop.prop2.prop3) -> ["myObj", "prop", "prop2", "prop3"]
* @example nameof.split(myObj.prop.prop2.prop3, -3);`, `["prop", "prop2", "prop3"];
* @example nameof.split(myObj.prop.prop2.prop3, 2);`, `["prop2", "prop3"]
*
* @param obj An expression for which the parts will be parsed.
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function split(obj: any, periodIndex?: number): string[];
}
================================================
FILE: packages/ts-nameof/tsconfig.json
================================================
{
"compilerOptions": {
"outDir": "./dist"
},
"extends": "../../tsconfig.common.json",
"include": ["./src"],
"references": [
{ "path": "../transforms-ts" },
{ "path": "../tests-common" },
{ "path": "../scripts-common" }
]
}
================================================
FILE: packages/ts-nameof.macro/.mocharc.yml
================================================
require: ts-node/register
recursive: true
reporter: progress
watch-extensions: ts
timeout: 10000
spec: src/tests/**/*.ts
================================================
FILE: packages/ts-nameof.macro/.npmignore
================================================
/node_modules
/.vscode
/.vs
/.git
/obj
/temp
/bin
/src
/dist/tests
/setup
/scripts
/lib
*.js.map
*.v12.suo
*.csproj
*.csproj.user
*.sln
*.log
.travis.yml
.gitignore
CHANGELOG.md
.mocharc.yml
tsconfig.json
tsconfig.tsbuildinfo
================================================
FILE: packages/ts-nameof.macro/LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2019 David Sherret
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: packages/ts-nameof.macro/README.md
================================================
# ts-nameof.macro
[](https://badge.fury.io/js/ts-nameof.macro)
[](https://travis-ci.org/dsherret/ts-nameof)
[](https://github.com/kentcdodds/babel-plugin-macros)
[](http://github.com/badges/stability-badges)
[`nameof`](https://msdn.microsoft.com/en-us/library/dn986596.aspx) in TypeScript.
This is a [babel macro](https://github.com/kentcdodds/babel-plugin-macros) of [ts-nameof](https://github.com/dsherret/ts-nameof).
## Setup
1. Install dependencies:
```
npm install --save-dev babel-plugin-macros ts-nameof.macro
```
2. Ensure `babel-plugin-macros` is properly setup ([Instructions](https://github.com/kentcdodds/babel-plugin-macros/blob/master/other/docs/user.md)).
3. Import and use the default export. For example:
```ts
import nameof from "ts-nameof.macro";
nameof(window.alert);
```
Transforms to:
```ts
"alert";
```
## Transforms
[Read here](https://github.com/dsherret/ts-nameof/blob/master/README.md)
## Other
- [Contributing](https://github.com/dsherret/ts-nameof/blob/master/CONTRIBUTING.md)
- [Development](https://github.com/dsherret/ts-nameof/blob/master/DEVELOPMENT.md)
================================================
FILE: packages/ts-nameof.macro/package.json
================================================
{
"name": "ts-nameof.macro",
"version": "4.2.2",
"description": "Babel macro for nameof in TypeScript.",
"main": "dist/index.js",
"types": "ts-nameof.macro.d.ts",
"scripts": {
"clean": "rimraf dist && tsc --b --clean",
"build": "tsc --b && npm run build:declarations",
"build:declarations": "ts-node --project scripts/tsconfig.json scripts/generation/main create-declaration-file",
"test": "tsc --build && mocha",
"test:debug": "npm run build && mocha --inspect-brk",
"dopublish": "npm run install && npm run build && echo \"Run: npm publish --otp\""
},
"keywords": [
"nameof",
"typescript",
"transforms",
"babel",
"babel-plugin-macros"
],
"repository": {
"type": "git",
"url": "git+https://github.com/dsherret/ts-nameof.git",
"directory": "packages/ts-nameof.macro"
},
"author": "David Sherret",
"license": "MIT",
"peerDependencies": {
"babel-plugin-macros": "^3.1.0"
},
"dependencies": {
"@ts-nameof/transforms-babel": "^4.2.1"
},
"devDependencies": {
"@babel/core": "^7.16.5",
"@babel/preset-typescript": "^7.16.5",
"@ts-nameof/scripts-common": "^4.0.2",
"@ts-nameof/tests-common": "^4.2.0",
"@types/babel__core": "^7.1.17",
"@types/babel__generator": "^7.6.3",
"@types/babel__template": "^7.4.1",
"@types/babel__traverse": "^7.14.2",
"@types/mocha": "^9.0.0",
"@types/node": "^17.0.0",
"babel-plugin-macros": "^3.1.0",
"mocha": "^9.1.3",
"rimraf": "^3.0.2",
"ts-morph": "^13.0.2",
"ts-node": "^10.4.0",
"typescript": "^4.5.4"
}
}
================================================
FILE: packages/ts-nameof.macro/scripts/common/createProject.ts
================================================
import { Project } from "ts-morph";
export function getProject() {
return new Project({ tsConfigFilePath: "tsconfig.json" });
}
================================================
FILE: packages/ts-nameof.macro/scripts/common/index.ts
================================================
export * from "./createProject";
================================================
FILE: packages/ts-nameof.macro/scripts/generation/createDeclarationFile.ts
================================================
import { ModuleDeclarationKind, Node, Project } from "ts-morph";
export function createDeclarationFile(project: Project) {
const globalFile = project.addSourceFileAtPath("../../lib/global.d.ts");
const declarationFile = project.createSourceFile("ts-nameof.macro.d.ts", "", { overwrite: true });
const namespaceDec = declarationFile.addModule({
name: `"ts-nameof.macro"`,
declarationKind: ModuleDeclarationKind.Module,
hasDeclareKeyword: true,
});
namespaceDec.setBodyText(globalFile.getFullText());
for (const statement of namespaceDec.getStatements()) {
if (Node.isAmbientable(statement)) {
statement.setHasDeclareKeyword(false);
}
}
namespaceDec.addExportAssignment({
expression: "nameof",
isExportEquals: false,
});
}
================================================
FILE: packages/ts-nameof.macro/scripts/generation/main.ts
================================================
import { ArgsChecker } from "@ts-nameof/scripts-common";
import { getProject } from "../common";
import { createDeclarationFile } from "./createDeclarationFile";
const argsChecker = new ArgsChecker();
const project = getProject();
if (argsChecker.checkHasArg("create-declaration-file")) {
console.log("Creating declaration file...");
createDeclarationFile(project);
}
argsChecker.verifyArgsUsed();
project.save();
================================================
FILE: packages/ts-nameof.macro/scripts/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es6",
"noEmit": true
},
"extends": "../../../tsconfig.common.json",
"exclude": [
"./lib",
"./src"
]
}
================================================
FILE: packages/ts-nameof.macro/src/index.js
================================================
/// @ts-check
/// <reference path="references.d.ts" />
import { transformNode } from "@ts-nameof/transforms-babel";
import { createMacro, MacroError } from "babel-plugin-macros";
export default createMacro(nameofMacro);
// @ts-ignore
function nameofMacro({ references, state, babel }) {
// go over in reverse as if traversing in post order
const reverseDefault = references.default.slice().reverse();
// @ts-ignore
reverseDefault.forEach(path => {
const t = babel.types;
transformNode(t, getPath(), {
// tell the transformation to expect this identifier's name
nameofIdentifierName: path.node.name,
});
function getPath() {
const parentPath = path.parentPath; // identifier;
if (parentPath.type === "CallExpression") {
return parentPath;
}
const grandParentPath = parentPath.parentPath;
if (parentPath.type === "MemberExpression" && grandParentPath.type === "CallExpression") {
return grandParentPath;
}
throw new MacroError("[ts-nameof]: Could not find a call expression at path: " + grandParentPath.getSource());
}
});
}
================================================
FILE: packages/ts-nameof.macro/src/references.d.ts
================================================
declare module "babel-plugin-macros";
================================================
FILE: packages/ts-nameof.macro/src/tests/macroTests.ts
================================================
/// <reference path="../references.d.ts" />
import * as babel from "@babel/core";
import "@babel/preset-typescript";
import { runCommonTests } from "@ts-nameof/tests-common";
import * as assert from "assert";
import babelPluginMacros from "babel-plugin-macros";
import * as path from "path";
runCommonTests(run, { commonPrefix: "import nameof from './ts-nameof.macro';\n" });
describe("using a name other than nameof", () => {
it("should work when using a different import name", () => {
const text = "import other from './ts-nameof.macro';other(console.log);other.full(console.log);";
assert.equal(run(text), `"log";"console.log";`);
});
});
function run(text: string) {
return babel.transformSync(text, {
presets: [
"@babel/preset-typescript",
],
plugins: [
babelPluginMacros,
],
filename: path.join(__dirname, "test.ts"),
ast: false,
generatorOpts: {
retainLines: true,
},
})!.code!;
}
================================================
FILE: packages/ts-nameof.macro/src/tests/ts-nameof.macro/index.js
================================================
/// @ts-check
// hack to get tests working
export { default } from "../../index";
================================================
FILE: packages/ts-nameof.macro/ts-nameof.macro.d.ts
================================================
declare module "ts-nameof.macro" {
/**
* Gets a string representation of the final identifier of the given expression.
*
* @example nameof<MyInterface>() -> "MyInterface"
* @example nameof<Array<MyInterface>>() -> "Array"
* @example nameof<MyNamespace.MyInnerInterface>() -> "MyInnerInterface"
* @example nameof<MyInterface>(o => o.prop) -> "prop"
*
* @param func An optional function for which the last identifier of the expression will be parsed.
*/
function nameof<T>(func?: (obj: T) => any): string;
/**
* Gets a string representation of the last identifier of the given expression.
*
* @example nameof(console) -> "console"
* @example nameof(console.log) -> "log"
* @example nameof(console["warn"]) -> "warn"
*
* @param obj An expression for which the last identifier will be parsed.
*/
function nameof(obj: any): string;
namespace nameof {
/**
* Gets the string representation of the entire type parameter expression.
*
* @example nameof.full<MyNamespace.MyInnerInterface>() -> "MyNamespace.MyInnerInterface"
* @example nameof.full<MyNamespace.MyInnerInterface>(1) -> "MyInnerInterface"
* @example nameof.full<Array<MyInterface>>() -> "Array"
* @example nameof.full<MyNamespace.AnotherNamespace.MyInnerInterface>>(-1) -> "MyInnerInterface"
*
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function full<T>(periodIndex?: number): string;
/**
* Gets the string representation of the entire resultant expression.
*
* @example nameof.full<MyInterface>(o => o.prop.prop2) -> "prop.prop2"
* @example nameof.full<MyInterface>(o => o.prop.prop2.prop3, 1) -> "prop2.prop3"
* @example nameof.full<MyInterface>(o => o.prop.prop2.prop3, -1) -> `"prop3"
*
* @param func A function for which the result will be parsed, excluding the parameter's identifier.
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function full<T>(func: (obj: T) => any, periodIndex?: number): string;
/**
* Gets the string representation of the entire given expression.
*
* @example nameof.full(console.log) -> "console.log"
* @example nameof.full(window.alert.length, -1) -> "length"
* @example nameof.full(window.alert.length, 2) -> "length"
*
* @param obj The expression which will be parsed.
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function full(obj: any, periodIndex?: number): string;
/**
* Gets an array containing the string representation of the final identifier of each expression in the array returned by the provided function.
*
* @example nameof.toArray<MyType>(o => [o.firstProp, o.otherProp.secondProp, o.other]) -> ["firstProp", "secondProp", "other"]
* @example nameof.toArray<MyType>(o => [o.prop, nameof.full(o.myProp.otherProp, 1)]) -> ["prop", "myProp.otherProp"]
*
* @param func A function returning an array of expressions to be parsed, excluding the parameter's identifier.
*/
function toArray<T>(func: (obj: T) => any[]): string[];
/**
* Gets an array containing the string representation of each expression in the arguments.
*
* @example nameof.toArray(myObject, otherObject) -> ["myObject", "otherObject"]
* @example nameof.toArray(obj.firstProp, obj.secondProp, otherObject, nameof.full(obj.other)) -> ["firstProp", "secondProp", "otherObject", "obj.other"]
*
* @param args An array of expressions to be parsed.
*/
function toArray(...args: any[]): string[];
/**
* Embeds an expression into the string representation of the result of nameof.full.
*
* @example nameof.full(myObj.prop[nameof.interpolate(i)]) -> `myObj.prop[${i}]`
*
* @param value The value to interpolate.
*/
function interpolate<T>(value: T): T;
/**
* Gets an array of strings where each element is a subsequent part of the expression provided.
*
* @example nameof.split<MyInterface>(o => o.prop.prop2.prop3) -> ["prop", "prop2", "prop3"]
* @example nameof.split<MyInterface>(o => o.prop.prop2.prop3, 1) -> ["prop2", "prop3"]
* @example nameof.split<MyInterface>(o => o.prop.prop2.prop3, -1) -> ["prop", "prop2"]
*
* @param func A function for which the resultant parts will be parsed, excluding the parameter's identifier.
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function split<T>(func: (obj: T) => any, periodIndex?: number): string[];
/**
* Gets an array of strings where each element is a subsequent part of the expression provided.
*
* @example nameof.split(myObj.prop.prop2.prop3) -> ["myObj", "prop", "prop2", "prop3"]
* @example nameof.split(myObj.prop.prop2.prop3, -3);`, `["prop", "prop2", "prop3"];
* @example nameof.split(myObj.prop.prop2.prop3, 2);`, `["prop2", "prop3"]
*
* @param obj An expression for which the parts will be parsed.
* @param periodIndex Specifies the index of the part of the expression to parse.
* When absent, the full expression will be parsed.
* A negative index can be used, indicating an offset from the end of the sequence.
*/
function split(obj: any, periodIndex?: number): string[];
}
export default nameof;
}
================================================
FILE: packages/ts-nameof.macro/tsconfig.json
================================================
{
"compilerOptions": {
"allowJs": true,
"outDir": "./dist"
},
"extends": "../../tsconfig.common.json",
"include": ["./src"],
"references": [
{ "path": "../transforms-babel" },
{ "path": "../tests-common" },
{ "path": "../scripts-common" }
]
}
================================================
FILE: tsconfig.common.json
================================================
{
"compilerOptions": {
"target": "es2015",
"module": "commonjs",
"declaration": false,
"sourceMap": true,
"strict": true,
"removeComments": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"lib": ["es2015", "DOM"],
"esModuleInterop": true
},
"compileOnSave": false
}
gitextract_ndkir86v/ ├── .gitignore ├── .travis.yml ├── .vscode/ │ └── launch.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DEVELOPMENT.md ├── LICENSE ├── README.md ├── dprint.json ├── lib/ │ ├── global.d.ts │ └── global.tests.ts ├── package.json ├── packages/ │ ├── babel-plugin-ts-nameof/ │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── common/ │ │ ├── .npmignore │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── errors.ts │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── scripts-common/ │ │ ├── .npmignore │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── ArgsChecker.ts │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── tests-common/ │ │ ├── .npmignore │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── runCommonTests.ts │ │ └── tsconfig.json │ ├── transforms-babel/ │ │ ├── .mocharc.yml │ │ ├── .npmignore │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── VisitSourceFileContext.ts │ │ │ ├── helpers.ts │ │ │ ├── index.ts │ │ │ ├── parse.ts │ │ │ ├── tests/ │ │ │ │ └── pluginTests.ts │ │ │ └── transform.ts │ │ └── tsconfig.json │ ├── transforms-common/ │ │ ├── .mocharc.yml │ │ ├── .npmignore │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── StringOrTemplateExpressionBuilder.ts │ │ │ ├── index.ts │ │ │ ├── nodeFactories.ts │ │ │ ├── nodeHelpers.ts │ │ │ ├── nodes.ts │ │ │ ├── printers.ts │ │ │ ├── tests/ │ │ │ │ └── printerTests.ts │ │ │ └── transformCallExpression.ts │ │ └── tsconfig.json │ ├── transforms-ts/ │ │ ├── .mocharc.yml │ │ ├── .npmignore │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── VisitSourceFileContext.ts │ │ │ ├── helpers.ts │ │ │ ├── index.ts │ │ │ ├── parse.ts │ │ │ ├── tests/ │ │ │ │ └── transformerFactoryTests.ts │ │ │ ├── transform.ts │ │ │ └── transformerFactory.ts │ │ └── tsconfig.json │ ├── ts-nameof/ │ │ ├── .mocharc.yml │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── lib/ │ │ │ └── declarationFileTests.ts │ │ ├── package.json │ │ ├── scripts/ │ │ │ ├── common/ │ │ │ │ ├── createProject.ts │ │ │ │ └── index.ts │ │ │ ├── generation/ │ │ │ │ ├── createDeclarationFile.ts │ │ │ │ └── main.ts │ │ │ ├── tsconfig.json │ │ │ └── verification/ │ │ │ ├── main.ts │ │ │ └── verifyDeclarationFile.ts │ │ ├── setup/ │ │ │ ├── custom.md │ │ │ ├── fusebox.md │ │ │ ├── gulp.md │ │ │ ├── jest.md │ │ │ ├── tsc.md │ │ │ └── webpack.md │ │ ├── src/ │ │ │ ├── main.ts │ │ │ ├── tests/ │ │ │ │ ├── testFiles/ │ │ │ │ │ ├── GeneralTestFile.txt │ │ │ │ │ ├── StreamNoNameofTestFile.txt │ │ │ │ │ ├── StreamTestFile.txt │ │ │ │ │ ├── globFolder/ │ │ │ │ │ │ └── MyGlobTestFile.txt │ │ │ │ │ └── issues/ │ │ │ │ │ ├── 11-expected.txt │ │ │ │ │ ├── 11-source.txt │ │ │ │ │ ├── 8-expected.txt │ │ │ │ │ └── 8-source.txt │ │ │ │ └── text/ │ │ │ │ ├── helpers/ │ │ │ │ │ ├── fileHelpers.ts │ │ │ │ │ ├── getTestFilePath.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── issuesTests.ts │ │ │ │ ├── replaceInFilesTests.ts │ │ │ │ └── replaceInTextTests.ts │ │ │ └── text/ │ │ │ ├── getFileNamesFromGlobs.ts │ │ │ ├── index.ts │ │ │ ├── replaceInFiles.ts │ │ │ └── replaceInText.ts │ │ ├── ts-nameof.d.ts │ │ └── tsconfig.json │ └── ts-nameof.macro/ │ ├── .mocharc.yml │ ├── .npmignore │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── scripts/ │ │ ├── common/ │ │ │ ├── createProject.ts │ │ │ └── index.ts │ │ ├── generation/ │ │ │ ├── createDeclarationFile.ts │ │ │ └── main.ts │ │ └── tsconfig.json │ ├── src/ │ │ ├── index.js │ │ ├── references.d.ts │ │ └── tests/ │ │ ├── macroTests.ts │ │ └── ts-nameof.macro/ │ │ └── index.js │ ├── ts-nameof.macro.d.ts │ └── tsconfig.json └── tsconfig.common.json
SYMBOL INDEX (104 symbols across 40 files)
FILE: lib/global.tests.ts
type TestType (line 3) | interface TestType {
class TestClass (line 8) | class TestClass {
FILE: packages/common/src/errors.ts
function throwError (line 1) | function throwError(message: string): never {
function throwErrorForSourceFile (line 5) | function throwErrorForSourceFile(message: string, sourceFilePath: string...
function assertNever (line 9) | function assertNever(value: never, message: string): never {
function sanitizeMessage (line 13) | function sanitizeMessage(message: string) {
FILE: packages/scripts-common/src/ArgsChecker.ts
class ArgsChecker (line 1) | class ArgsChecker {
method constructor (line 5) | constructor(args?: string[]) {
method checkHasArg (line 10) | checkHasArg(argName: string) {
method checkHasExplicitArg (line 18) | checkHasExplicitArg(argName: string) {
method verifyArgsUsed (line 28) | verifyArgsUsed() {
FILE: packages/tests-common/src/runCommonTests.ts
function runCommonTests (line 14) | function runCommonTests(getTransformedText: (text: string) => string, op...
function getFirstAccessedPropertyMustNotBeComputedErrorText (line 594) | function getFirstAccessedPropertyMustNotBeComputedErrorText(nodeText: st...
function getNotSupportedErrorText (line 598) | function getNotSupportedErrorText(nodeText: string) {
function getUnusedNameofInterpolateErrorText (line 602) | function getUnusedNameofInterpolateErrorText(nodeText: string) {
FILE: packages/transforms-babel/src/VisitSourceFileContext.ts
type VisitSourceFileContext (line 3) | interface VisitSourceFileContext {
FILE: packages/transforms-babel/src/helpers.ts
function isNegativeNumericLiteral (line 5) | function isNegativeNumericLiteral(t: typeof babelTypes, node: Node): nod...
function getNegativeNumericLiteralValue (line 13) | function getNegativeNumericLiteralValue(t: typeof babelTypes, node: Unar...
function getReturnStatementArgumentFromBlock (line 21) | function getReturnStatementArgumentFromBlock(t: typeof babelTypes, block...
FILE: packages/transforms-babel/src/index.ts
type TransformOptions (line 9) | interface TransformOptions extends ParseOptions {
function plugin (line 12) | function plugin({ types: t }: { types: typeof babelTypes }): babel.Plugi...
function transformNode (line 29) | function transformNode(t: typeof babelTypes, path: NodePath, options: Tr...
FILE: packages/transforms-babel/src/parse.ts
type ParseOptions (line 25) | interface ParseOptions {
function parse (line 45) | function parse(t: typeof babelTypes, path: NodePath, options: ParseOptio...
FILE: packages/transforms-babel/src/tests/pluginTests.ts
function run (line 9) | function run(text: string) {
FILE: packages/transforms-babel/src/transform.ts
function transform (line 9) | function transform(t: typeof babelTypes, node: common.Node): babelTypes....
function createTemplateLiteral (line 22) | function createTemplateLiteral(t: typeof babelTypes, node: common.Templa...
FILE: packages/transforms-common/src/StringOrTemplateExpressionBuilder.ts
class StringOrTemplateExpressionNodeBuilder (line 8) | class StringOrTemplateExpressionNodeBuilder {
method hasText (line 12) | hasText() {
method buildNode (line 16) | buildNode(): StringLiteralNode | TemplateExpressionNode {
method addItem (line 23) | addItem(item: string | InterpolateNode | StringLiteralNode | TemplateE...
method addText (line 37) | addText(newText: string) {
method addInterpolate (line 49) | private addInterpolate(interpolate: InterpolateNode) {
FILE: packages/transforms-common/src/nodeFactories.ts
function createIdentifierNode (line 14) | function createIdentifierNode(value: string, next?: Node | undefined): I...
function createStringLiteralNode (line 22) | function createStringLiteralNode(value: string, next?: Node | undefined)...
function createNumericLiteralNode (line 30) | function createNumericLiteralNode(value: number, next?: Node | undefined...
function createArrayLiteralNode (line 38) | function createArrayLiteralNode(elements: ArrayLiteralNode["elements"], ...
function createComputedNode (line 46) | function createComputedNode(value: Node, next?: Node | undefined): Compu...
function createFunctionNode (line 54) | function createFunctionNode(value: Node, parameterNames: string[], next?...
function createImportTypeNode (line 63) | function createImportTypeNode(isTypeOf: boolean, argument: Node | undefi...
function createTemplateExpressionNode (line 72) | function createTemplateExpressionNode(parts: (string | InterpolateNode)[...
function createInterpolateNode (line 80) | function createInterpolateNode(expression: unknown, expressionText: stri...
FILE: packages/transforms-common/src/nodeHelpers.ts
function flattenNodeToArray (line 3) | function flattenNodeToArray(node: Node) {
function getLastNextNode (line 12) | function getLastNextNode(node: Node) {
FILE: packages/transforms-common/src/nodes.ts
type NameofCallExpression (line 3) | interface NameofCallExpression {
type Node (line 9) | type Node =
type IdentifierNode (line 20) | interface IdentifierNode {
type StringLiteralNode (line 26) | interface StringLiteralNode {
type NumericLiteralNode (line 32) | interface NumericLiteralNode {
type ArrayLiteralNode (line 38) | interface ArrayLiteralNode {
type ComputedNode (line 48) | interface ComputedNode {
type FunctionNode (line 54) | interface FunctionNode {
type ImportTypeNode (line 61) | interface ImportTypeNode {
type TemplateExpressionNode (line 68) | interface TemplateExpressionNode {
type InterpolateNode (line 78) | interface InterpolateNode {
FILE: packages/transforms-common/src/printers.ts
function printCallExpression (line 8) | function printCallExpression(callExpr: NameofCallExpression) {
function printNode (line 52) | function printNode(node: Node): string {
FILE: packages/transforms-common/src/tests/printerTests.ts
function doTest (line 7) | function doTest(callExpr: NameofCallExpression, expectedText: string) {
function doTest (line 82) | function doTest(node: Node, expectedText: string) {
FILE: packages/transforms-common/src/transformCallExpression.ts
function transformCallExpression (line 8) | function transformCallExpression(callExpr: NameofCallExpression) {
function handleNameof (line 24) | function handleNameof(callExpr: NameofCallExpression) {
function handleNameofFull (line 37) | function handleNameofFull(callExpr: NameofCallExpression) {
function handleNameofSplit (line 41) | function handleNameofSplit(callExpr: NameofCallExpression) {
function handleNameofToArray (line 46) | function handleNameofToArray(callExpr: NameofCallExpression) {
function getNodesFromCallExpression (line 74) | function getNodesFromCallExpression(callExpr: NameofCallExpression) {
function parseNameofExpression (line 148) | function parseNameofExpression(expression: Node) {
function parseNode (line 164) | function parseNode(node: Node, parent?: Node) {
function parseNameofFullExpression (line 193) | function parseNameofFullExpression(expressionNodes: Node[]): StringLiter...
function throwNotSupportedErrorForNode (line 247) | function throwNotSupportedErrorForNode(node: Node) {
FILE: packages/transforms-ts/src/VisitSourceFileContext.ts
type VisitSourceFileContext (line 3) | interface VisitSourceFileContext {
FILE: packages/transforms-ts/src/helpers.ts
function isNegativeNumericLiteral (line 4) | function isNegativeNumericLiteral(node: ts.Node): node is ts.PrefixUnary...
function getNegativeNumericLiteralValue (line 13) | function getNegativeNumericLiteralValue(node: ts.PrefixUnaryExpression) {
function getReturnStatementExpressionFromBlock (line 25) | function getReturnStatementExpressionFromBlock(block: ts.Block) {
function getNodeText (line 38) | function getNodeText(node: ts.Node, sourceFile: ts.SourceFile) {
FILE: packages/transforms-ts/src/parse.ts
function parse (line 15) | function parse(parsingNode: ts.Node, sourceFile: ts.SourceFile, context:...
FILE: packages/transforms-ts/src/tests/transformerFactoryTests.ts
function run (line 7) | function run(text: string) {
FILE: packages/transforms-ts/src/transform.ts
type TransformResult (line 9) | type TransformResult = ts.StringLiteral | ts.ArrayLiteralExpression | ts...
function transform (line 15) | function transform(node: common.Node, context: VisitSourceFileContext | ...
function createTemplateExpression (line 31) | function createTemplateExpression(node: common.TemplateExpressionNode, c...
FILE: packages/transforms-ts/src/transformerFactory.ts
function visitSourceFile (line 15) | function visitSourceFile(sourceFile: ts.SourceFile, context: ts.Transfor...
function throwIfContextHasInterpolateExpressions (line 45) | function throwIfContextHasInterpolateExpressions(context: VisitSourceFil...
function visitNode (line 59) | function visitNode(visitingNode: ts.Node, sourceFile: ts.SourceFile, con...
FILE: packages/ts-nameof.macro/scripts/common/createProject.ts
function getProject (line 3) | function getProject() {
FILE: packages/ts-nameof.macro/scripts/generation/createDeclarationFile.ts
function createDeclarationFile (line 3) | function createDeclarationFile(project: Project) {
FILE: packages/ts-nameof.macro/src/index.js
function nameofMacro (line 9) | function nameofMacro({ references, state, babel }) {
FILE: packages/ts-nameof.macro/src/tests/macroTests.ts
function run (line 18) | function run(text: string) {
FILE: packages/ts-nameof/lib/declarationFileTests.ts
function testFunc (line 8) | function testFunc() {
FILE: packages/ts-nameof/scripts/common/createProject.ts
function getProject (line 3) | function getProject() {
FILE: packages/ts-nameof/scripts/generation/createDeclarationFile.ts
function createDeclarationFile (line 3) | function createDeclarationFile(project: Project) {
FILE: packages/ts-nameof/scripts/verification/verifyDeclarationFile.ts
function verifyDeclarationFile (line 3) | function verifyDeclarationFile() {
FILE: packages/ts-nameof/src/main.ts
type Api (line 5) | interface Api {
FILE: packages/ts-nameof/src/tests/text/helpers/fileHelpers.ts
function readFile (line 3) | function readFile(path: string) {
function writeFile (line 16) | function writeFile(path: string, contents: string) {
FILE: packages/ts-nameof/src/tests/text/helpers/getTestFilePath.ts
function getTestFilePath (line 3) | function getTestFilePath(...paths: string[]) {
FILE: packages/ts-nameof/src/tests/text/issuesTests.ts
function runTest (line 6) | async function runTest(fileName: string, expectedFileName: string) {
function runIssueTest (line 22) | function runIssueTest(issueNumber: number) {
FILE: packages/ts-nameof/src/tests/text/replaceInFilesTests.ts
type FileInfo (line 6) | interface FileInfo {
function runTest (line 11) | async function runTest(paths: string[], expectedFiles: FileInfo[]) {
FILE: packages/ts-nameof/src/text/getFileNamesFromGlobs.ts
function getFileNamesFromGlobs (line 3) | function getFileNamesFromGlobs(globs: ReadonlyArray<string>) {
function getFileNamesFromGlob (line 9) | function getFileNamesFromGlob(globFileName: string) {
FILE: packages/ts-nameof/src/text/replaceInFiles.ts
function replaceInFiles (line 5) | function replaceInFiles(fileNames: ReadonlyArray<string>): Promise<void[...
function doReplaceInFiles (line 9) | function doReplaceInFiles(fileNames: ReadonlyArray<string>) {
FILE: packages/ts-nameof/src/text/replaceInText.ts
function replaceInText (line 6) | function replaceInText(fileName: string, fileText: string): { fileText?:...
FILE: packages/ts-nameof/ts-nameof.d.ts
type Api (line 2) | interface Api {
Condensed preview — 129 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (185K chars).
[
{
"path": ".gitignore",
"chars": 1144,
"preview": "/node_modules\r\n/.vs\r\n/bin\r\n/dist\r\n/obj\r\n/temp\r\n/temp\r\n*.js.map\r\n*.suo\r\n*.csproj\r\n*.csproj.user\r\n*.sln\r\n/packages/ts-name"
},
{
"path": ".travis.yml",
"chars": 113,
"preview": "language: node_js\r\nnode_js:\r\n - '16'\r\nscript:\r\n - set -e\r\n\r\n - yarn install\r\n - yarn build\r\n - yarn verify\r\n"
},
{
"path": ".vscode/launch.json",
"chars": 1391,
"preview": "{\n \"version\": \"0.2.0\",\n \"configurations\": [\n {\n \"type\": \"node\",\n \"request\": \"launch\",\n \"name\": \"Run "
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3408,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CONTRIBUTING.md",
"chars": 1640,
"preview": "# Logging Bugs\n\n1. Start logging an issue in the [issue tracker](https://github.com/dsherret/ts-nameof/issues).\n2. Clear"
},
{
"path": "DEVELOPMENT.md",
"chars": 2075,
"preview": "# Development\n\n## Building\n\nOpen the root directory of the repo and run:\n\n```bash\n# install dependencies\nyarn install\n# "
},
{
"path": "LICENSE",
"chars": 1080,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2019 David Sherret\n\nPermission is hereby granted, free of charge, to any person obt"
},
{
"path": "README.md",
"chars": 3992,
"preview": "# ts-nameof\n\n[](https://travis-ci.org/dsherret/ts-nameof)\n\n"
},
{
"path": "dprint.json",
"chars": 374,
"preview": "{\n \"incremental\": true,\n \"lineWidth\": 160,\n \"indentWidth\": 2,\n \"includes\": [\"**/*.{ts,tsx,js,jsx,json,md}\"],\n \"excl"
},
{
"path": "lib/global.d.ts",
"chars": 5758,
"preview": "/**\n * Gets a string representation of the final identifier of the given expression.\n *\n * @example nameof<MyInterface>("
},
{
"path": "lib/global.tests.ts",
"chars": 2090,
"preview": "/// <reference path=\"./global.d.ts\" />\nnamespace TestNamespace {\n export interface TestType {\n prop: string;\n }\n}\n\n"
},
{
"path": "package.json",
"chars": 770,
"preview": "{\n \"name\": \"ts-nameof-workspace\",\n \"private\": true,\n \"workspaces\": [\n \"packages/common\",\n \"packages/scripts-com"
},
{
"path": "packages/babel-plugin-ts-nameof/.npmignore",
"chars": 205,
"preview": "/node_modules\n/.vscode\n/.vs\n/.git\n/obj\n/temp\n/bin\n/src\n/dist/tests\n/setup\n/scripts\n/lib\n*.js.map\n*.v12.suo\n*.csproj\n*.cs"
},
{
"path": "packages/babel-plugin-ts-nameof/LICENSE",
"chars": 1080,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2019 David Sherret\n\nPermission is hereby granted, free of charge, to any person obt"
},
{
"path": "packages/babel-plugin-ts-nameof/README.md",
"chars": 919,
"preview": "# babel-plugin-ts-nameof\n\n[](https://badge.fury.io/j"
},
{
"path": "packages/babel-plugin-ts-nameof/package.json",
"chars": 1133,
"preview": "{\n \"name\": \"babel-plugin-ts-nameof\",\n \"version\": \"4.2.1\",\n \"description\": \"nameof in TypeScript for babel.\",\n \"main\""
},
{
"path": "packages/babel-plugin-ts-nameof/src/index.ts",
"chars": 65,
"preview": "export { plugin as default } from \"@ts-nameof/transforms-babel\";\n"
},
{
"path": "packages/babel-plugin-ts-nameof/tsconfig.json",
"chars": 217,
"preview": "{\n \"compilerOptions\": {\n \"outDir\": \"./dist\"\n },\n \"extends\": \"../../tsconfig.common.json\",\n \"include\": [\"./src\"],\n"
},
{
"path": "packages/common/.npmignore",
"chars": 59,
"preview": "tsconfig.json\r\ntsconfig.tsbuildinfo\r\n*.log\r\n*.js.map\r\nsrc\r\n"
},
{
"path": "packages/common/README.md",
"chars": 58,
"preview": "# ts-nameof - Common\n\nContains common utilities functions."
},
{
"path": "packages/common/package.json",
"chars": 467,
"preview": "{\n \"name\": \"@ts-nameof/common\",\n \"version\": \"4.2.1\",\n \"description\": \"ts-nameof - Common code across packages.\",\n \"m"
},
{
"path": "packages/common/src/errors.ts",
"chars": 505,
"preview": "export function throwError(message: string): never {\n throw new Error(`[ts-nameof]: ${sanitizeMessage(message)}`);\n}\n"
},
{
"path": "packages/common/src/index.ts",
"chars": 26,
"preview": "export * from \"./errors\";\n"
},
{
"path": "packages/common/tsconfig.json",
"chars": 177,
"preview": "{\n \"compilerOptions\": {\n \"composite\": true,\n \"declaration\": true,\n \"outDir\": \"./dist\"\n },\n \"extends\": \"../.."
},
{
"path": "packages/scripts-common/.npmignore",
"chars": 57,
"preview": "tsconfig.json\r\ntsconfig.tsbuildinfo\r\nsrc\r\n*.log\r\n*.js.map"
},
{
"path": "packages/scripts-common/README.md",
"chars": 72,
"preview": "# ts-nameof - Scripts Common\n\nContains scripts for use across packages.\n"
},
{
"path": "packages/scripts-common/package.json",
"chars": 520,
"preview": "{\n \"name\": \"@ts-nameof/scripts-common\",\n \"version\": \"4.0.2\",\n \"description\": \"ts-nameof - Common scripts for ts-nameo"
},
{
"path": "packages/scripts-common/src/ArgsChecker.ts",
"chars": 726,
"preview": "export class ArgsChecker {\n private readonly originalArgs: ReadonlyArray<string>;\n private readonly args: string[];\n\n "
},
{
"path": "packages/scripts-common/src/index.ts",
"chars": 31,
"preview": "export * from \"./ArgsChecker\";\n"
},
{
"path": "packages/scripts-common/tsconfig.json",
"chars": 177,
"preview": "{\n \"compilerOptions\": {\n \"composite\": true,\n \"declaration\": true,\n \"outDir\": \"./dist\"\n },\n \"extends\": \"../.."
},
{
"path": "packages/tests-common/.npmignore",
"chars": 59,
"preview": "tsconfig.json\r\ntsconfig.tsbuildinfo\r\nsrc\r\n*.log\r\n*.js.map\r\n"
},
{
"path": "packages/tests-common/README.md",
"chars": 143,
"preview": "# ts-nameof - Tests Common\n\nContains tests that should work across any implementation of ts-nameof (whether babel or the"
},
{
"path": "packages/tests-common/package.json",
"chars": 576,
"preview": "{\n \"name\": \"@ts-nameof/tests-common\",\n \"version\": \"4.2.0\",\n \"description\": \"Common tests for ts-nameof packages.\",\n "
},
{
"path": "packages/tests-common/src/index.ts",
"chars": 34,
"preview": "export * from \"./runCommonTests\";\n"
},
{
"path": "packages/tests-common/src/runCommonTests.ts",
"chars": 23355,
"preview": "import { createFromBuffer } from \"@dprint/formatter\";\n// @ts-ignore\nimport { getBuffer } from \"@dprint/typescript\";\nimpo"
},
{
"path": "packages/tests-common/tsconfig.json",
"chars": 177,
"preview": "{\n \"compilerOptions\": {\n \"composite\": true,\n \"declaration\": true,\n \"outDir\": \"./dist\"\n },\n \"extends\": \"../.."
},
{
"path": "packages/transforms-babel/.mocharc.yml",
"chars": 121,
"preview": "require: ts-node/register\nrecursive: true\nreporter: progress\nwatch-extensions: ts\ntimeout: 10000\nspec: src/tests/**/*.ts"
},
{
"path": "packages/transforms-babel/.npmignore",
"chars": 73,
"preview": "tsconfig.json\r\ntsconfig.tsbuildinfo\r\nsrc\r\n*.log\r\n*.js.map\r\n.mocharc.yml\r\n"
},
{
"path": "packages/transforms-babel/README.md",
"chars": 128,
"preview": "# ts-nameof - Babel transforms\n\nContains the babel transforms used in ts-nameof.\n\n## Development Commands\n\n```\nnpm run t"
},
{
"path": "packages/transforms-babel/package.json",
"chars": 1019,
"preview": "{\n \"name\": \"@ts-nameof/transforms-babel\",\n \"version\": \"4.2.1\",\n \"description\": \"ts-nameof - Babel transforms for ts-n"
},
{
"path": "packages/transforms-babel/src/VisitSourceFileContext.ts",
"chars": 119,
"preview": "import { Node } from \"@babel/types\";\n\nexport interface VisitSourceFileContext {\n interpolateExpressions: Set<Node>;\n}\n"
},
{
"path": "packages/transforms-babel/src/helpers.ts",
"chars": 975,
"preview": "import * as babelTypes from \"@babel/types\";\nimport { BlockStatement, Node, UnaryExpression } from \"@babel/types\";\nimport"
},
{
"path": "packages/transforms-babel/src/index.ts",
"chars": 1387,
"preview": "import * as babel from \"@babel/core\";\nimport { Node, NodePath } from \"@babel/traverse\";\nimport * as babelTypes from \"@ba"
},
{
"path": "packages/transforms-babel/src/parse.ts",
"chars": 10858,
"preview": "import { NodePath } from \"@babel/traverse\";\nimport * as babelTypes from \"@babel/types\";\nimport {\n ArrayExpression,\n Ar"
},
{
"path": "packages/transforms-babel/src/tests/pluginTests.ts",
"chars": 511,
"preview": "import * as babel from \"@babel/core\";\nimport \"@babel/preset-typescript\";\nimport { runCommonTests } from \"@ts-nameof/test"
},
{
"path": "packages/transforms-babel/src/transform.ts",
"chars": 1731,
"preview": "import * as babelTypes from \"@babel/types\";\nimport { throwError } from \"@ts-nameof/common\";\nimport * as common from \"@ts"
},
{
"path": "packages/transforms-babel/tsconfig.json",
"chars": 295,
"preview": "{\n \"compilerOptions\": {\n \"composite\": true,\n \"declaration\": true,\n \"outDir\": \"./dist\"\n },\n \"extends\": \"../.."
},
{
"path": "packages/transforms-common/.mocharc.yml",
"chars": 121,
"preview": "require: ts-node/register\nrecursive: true\nreporter: progress\nwatch-extensions: ts\ntimeout: 10000\nspec: src/tests/**/*.ts"
},
{
"path": "packages/transforms-common/.npmignore",
"chars": 83,
"preview": "tsconfig.json\r\ntsconfig.tsbuildinfo\r\nsrc\r\ndist/tests\r\n.mocharc.yml\r\n*.log\r\n*.js.map"
},
{
"path": "packages/transforms-common/README.md",
"chars": 99,
"preview": "# ts-nameof - Transforms Common\n\nContains the code shared between babel and typescript transforms.\n"
},
{
"path": "packages/transforms-common/package.json",
"chars": 687,
"preview": "{\n \"name\": \"@ts-nameof/transforms-common\",\n \"version\": \"4.2.1\",\n \"description\": \"ts-nameof - Common code for transfor"
},
{
"path": "packages/transforms-common/src/StringOrTemplateExpressionBuilder.ts",
"chars": 1649,
"preview": "import { createStringLiteralNode, createTemplateExpressionNode } from \"./nodeFactories\";\nimport { InterpolateNode, Strin"
},
{
"path": "packages/transforms-common/src/index.ts",
"chars": 101,
"preview": "export * from \"./nodeFactories\";\nexport * from \"./nodes\";\nexport * from \"./transformCallExpression\";\n"
},
{
"path": "packages/transforms-common/src/nodeFactories.ts",
"chars": 1880,
"preview": "import {\n ArrayLiteralNode,\n ComputedNode,\n FunctionNode,\n IdentifierNode,\n ImportTypeNode,\n InterpolateNode,\n No"
},
{
"path": "packages/transforms-common/src/nodeHelpers.ts",
"chars": 362,
"preview": "import { Node } from \"./nodes\";\n\nexport function flattenNodeToArray(node: Node) {\n const flattenedNodes: Node[] = [node"
},
{
"path": "packages/transforms-common/src/nodes.ts",
"chars": 1700,
"preview": "// common AST to share between babel and typescript\n\nexport interface NameofCallExpression {\n property: string | undefi"
},
{
"path": "packages/transforms-common/src/printers.ts",
"chars": 3009,
"preview": "import { assertNever } from \"@ts-nameof/common\";\nimport { NameofCallExpression, Node, TemplateExpressionNode } from \"./n"
},
{
"path": "packages/transforms-common/src/tests/printerTests.ts",
"chars": 7441,
"preview": "import * as assert from \"assert\";\nimport * as factories from \"../nodeFactories\";\nimport { NameofCallExpression, Node } f"
},
{
"path": "packages/transforms-common/src/transformCallExpression.ts",
"chars": 8617,
"preview": "import { assertNever, throwError } from \"@ts-nameof/common\";\nimport { createArrayLiteralNode, createStringLiteralNode, c"
},
{
"path": "packages/transforms-common/tsconfig.json",
"chars": 228,
"preview": "{\n \"compilerOptions\": {\n \"composite\": true,\n \"declaration\": true,\n \"outDir\": \"./dist\"\n },\n \"extends\": \"../.."
},
{
"path": "packages/transforms-ts/.mocharc.yml",
"chars": 121,
"preview": "require: ts-node/register\nrecursive: true\nreporter: progress\nwatch-extensions: ts\ntimeout: 10000\nspec: src/tests/**/*.ts"
},
{
"path": "packages/transforms-ts/.npmignore",
"chars": 85,
"preview": "tsconfig.json\r\ntsconfig.tsbuildinfo\r\nsrc\r\ndist/tests\r\n.mocharc.yml\r\n*.log\r\n*.js.map\r\n"
},
{
"path": "packages/transforms-ts/README.md",
"chars": 151,
"preview": "# ts-nameof - TypeScript transforms\n\nContains the TypeScript Compiler API transforms used in ts-nameof.\n\n## Development "
},
{
"path": "packages/transforms-ts/package.json",
"chars": 737,
"preview": "{\n \"name\": \"@ts-nameof/transforms-ts\",\n \"version\": \"4.2.1\",\n \"description\": \"ts-nameof - TypeScript compiler transfor"
},
{
"path": "packages/transforms-ts/src/VisitSourceFileContext.ts",
"chars": 119,
"preview": "import * as ts from \"typescript\";\n\nexport interface VisitSourceFileContext {\n interpolateExpressions: Set<ts.Node>;\n}\n"
},
{
"path": "packages/transforms-ts/src/helpers.ts",
"chars": 1347,
"preview": "import { throwError } from \"@ts-nameof/common\";\nimport * as ts from \"typescript\";\n\nexport function isNegativeNumericLite"
},
{
"path": "packages/transforms-ts/src/index.ts",
"chars": 152,
"preview": "export { TransformResult } from \"./transform\";\nexport * from \"./transformerFactory\";\nexport { VisitSourceFileContext } f"
},
{
"path": "packages/transforms-ts/src/parse.ts",
"chars": 9750,
"preview": "import { assertNever, throwError } from \"@ts-nameof/common\";\nimport * as common from \"@ts-nameof/transforms-common\";\nimp"
},
{
"path": "packages/transforms-ts/src/tests/transformerFactoryTests.ts",
"chars": 1488,
"preview": "import { runCommonTests } from \"@ts-nameof/tests-common\";\nimport * as ts from \"typescript\";\nimport { transformerFactory "
},
{
"path": "packages/transforms-ts/src/transform.ts",
"chars": 2554,
"preview": "import { throwError } from \"@ts-nameof/common\";\nimport * as common from \"@ts-nameof/transforms-common\";\nimport * as ts f"
},
{
"path": "packages/transforms-ts/src/transformerFactory.ts",
"chars": 2665,
"preview": "import { throwError, throwErrorForSourceFile } from \"@ts-nameof/common\";\nimport { transformCallExpression } from \"@ts-na"
},
{
"path": "packages/transforms-ts/tsconfig.json",
"chars": 303,
"preview": "{\n \"compilerOptions\": {\n \"composite\": true,\n \"declaration\": true,\n \"outDir\": \"./dist\"\n },\n \"extends\": \"../.."
},
{
"path": "packages/ts-nameof/.mocharc.yml",
"chars": 121,
"preview": "require: ts-node/register\nrecursive: true\nreporter: progress\nwatch-extensions: ts\ntimeout: 10000\nspec: src/tests/**/*.ts"
},
{
"path": "packages/ts-nameof/.npmignore",
"chars": 273,
"preview": "/node_modules\r\n/.vscode\r\n/.vs\r\n/.git\r\n/obj\r\n/temp\r\n/bin\r\n/src\r\n/dist/tests\r\n/setup\r\n/scripts\r\n/lib\r\n*.js.map\r\n*.v12.suo\r"
},
{
"path": "packages/ts-nameof/LICENSE",
"chars": 1080,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2019 David Sherret\n\nPermission is hereby granted, free of charge, to any person obt"
},
{
"path": "packages/ts-nameof/README.md",
"chars": 1773,
"preview": "# ts-nameof\n\n[](https://badge.fury.io/js/ts-nameof)\n[, specify it as a c"
},
{
"path": "packages/ts-nameof/setup/gulp.md",
"chars": 442,
"preview": "# Using ts-nameof with Gulp\n\nSpecify it as a custom transformer with [gulp-typescript](https://github.com/ivogabe/gulp-"
},
{
"path": "packages/ts-nameof/setup/jest.md",
"chars": 475,
"preview": "# Using ts-nameof with Jest\n\n1. Setup jest with [ts-jest](https://github.com/kulshekhar/ts-jest)\n\n2. `npm install --sav"
},
{
"path": "packages/ts-nameof/setup/tsc.md",
"chars": 1189,
"preview": "# Using ts-nameof with tsc\n\nTransformation plugins are currently not supported by `tsc` alone. Please go and upvote [thi"
},
{
"path": "packages/ts-nameof/setup/webpack.md",
"chars": 632,
"preview": "# Using ts-nameof with Webpack\n\n## ts-loader / awesome-typescript-loader\n\nIf using [ts-loader](https://github.com/TypeSt"
},
{
"path": "packages/ts-nameof/src/main.ts",
"chars": 612,
"preview": "import { transformerFactory } from \"@ts-nameof/transforms-ts\";\nimport * as ts from \"typescript\";\nimport { replaceInFiles"
},
{
"path": "packages/ts-nameof/src/tests/testFiles/GeneralTestFile.txt",
"chars": 1121,
"preview": "console.log(nameof(alert));\nconsole.log(nameof(window.alert));\nconsole.log(nameof.full(window.alert));\nconsole.log(nameo"
},
{
"path": "packages/ts-nameof/src/tests/testFiles/StreamNoNameofTestFile.txt",
"chars": 17,
"preview": "console.log(\"\");\n"
},
{
"path": "packages/ts-nameof/src/tests/testFiles/StreamTestFile.txt",
"chars": 16,
"preview": "nameof(window);\n"
},
{
"path": "packages/ts-nameof/src/tests/testFiles/globFolder/MyGlobTestFile.txt",
"chars": 30,
"preview": "console.log(nameof(console));\n"
},
{
"path": "packages/ts-nameof/src/tests/testFiles/issues/11-expected.txt",
"chars": 170,
"preview": "class Test {\n private x: Test;\n public y: Test;\n public z: string;\n\n constructor() {\n \"x\";\n \"y"
},
{
"path": "packages/ts-nameof/src/tests/testFiles/issues/11-source.txt",
"chars": 224,
"preview": "class Test {\n private x: Test;\n public y: Test;\n public z: string;\n\n constructor() {\n nameof(this.x);"
},
{
"path": "packages/ts-nameof/src/tests/testFiles/issues/8-expected.txt",
"chars": 1244,
"preview": "import CodeBlockWriter from \"code-block-writer\";\nimport {expect} from \"chai\";\nimport {FunctionDefinition, InterfaceMetho"
},
{
"path": "packages/ts-nameof/src/tests/testFiles/issues/8-source.txt",
"chars": 1306,
"preview": "import CodeBlockWriter from \"code-block-writer\";\nimport {expect} from \"chai\";\nimport {FunctionDefinition, InterfaceMetho"
},
{
"path": "packages/ts-nameof/src/tests/text/helpers/fileHelpers.ts",
"chars": 531,
"preview": "import * as fs from \"fs\";\n\nexport function readFile(path: string) {\n return new Promise<string>((resolve, reject) => {\n"
},
{
"path": "packages/ts-nameof/src/tests/text/helpers/getTestFilePath.ts",
"chars": 137,
"preview": "import * as path from \"path\";\n\nexport function getTestFilePath(...paths: string[]) {\n return path.join(\"./temp/testFile"
},
{
"path": "packages/ts-nameof/src/tests/text/helpers/index.ts",
"chars": 66,
"preview": "export * from \"./fileHelpers\";\nexport * from \"./getTestFilePath\";\n"
},
{
"path": "packages/ts-nameof/src/tests/text/issuesTests.ts",
"chars": 1053,
"preview": "import * as assert from \"assert\";\nimport { replaceInFiles } from \"../../text\";\nimport { getTestFilePath, readFile, write"
},
{
"path": "packages/ts-nameof/src/tests/text/replaceInFilesTests.ts",
"chars": 2673,
"preview": "import * as assert from \"assert\";\nimport { replaceInFiles } from \"../../text\";\nimport { getTestFilePath, readFile, write"
},
{
"path": "packages/ts-nameof/src/tests/text/replaceInTextTests.ts",
"chars": 1203,
"preview": "import { runCommonTests } from \"@ts-nameof/tests-common\";\nimport * as assert from \"assert\";\nimport { replaceInText } fro"
},
{
"path": "packages/ts-nameof/src/text/getFileNamesFromGlobs.ts",
"chars": 530,
"preview": "import glob from \"glob\";\n\nexport function getFileNamesFromGlobs(globs: ReadonlyArray<string>) {\n const promises = globs"
},
{
"path": "packages/ts-nameof/src/text/index.ts",
"chars": 108,
"preview": "export * from \"./getFileNamesFromGlobs\";\nexport * from \"./replaceInFiles\";\nexport * from \"./replaceInText\";\n"
},
{
"path": "packages/ts-nameof/src/text/replaceInFiles.ts",
"chars": 1192,
"preview": "import * as fs from \"fs\";\nimport { getFileNamesFromGlobs } from \"./getFileNamesFromGlobs\";\nimport { replaceInText } from"
},
{
"path": "packages/ts-nameof/src/text/replaceInText.ts",
"chars": 2678,
"preview": "import { throwIfContextHasInterpolateExpressions, visitNode, VisitSourceFileContext } from \"@ts-nameof/transforms-ts\";\ni"
},
{
"path": "packages/ts-nameof/ts-nameof.d.ts",
"chars": 5661,
"preview": "declare module \"ts-nameof\" {\n interface Api {\n (): any /* ts.TransformerFactory<ts.SourceFile> */;\n replaceInFile"
},
{
"path": "packages/ts-nameof/tsconfig.json",
"chars": 251,
"preview": "{\n \"compilerOptions\": {\n \"outDir\": \"./dist\"\n },\n \"extends\": \"../../tsconfig.common.json\",\n \"include\": [\"./src\"],\n"
},
{
"path": "packages/ts-nameof.macro/.mocharc.yml",
"chars": 121,
"preview": "require: ts-node/register\nrecursive: true\nreporter: progress\nwatch-extensions: ts\ntimeout: 10000\nspec: src/tests/**/*.ts"
},
{
"path": "packages/ts-nameof.macro/.npmignore",
"chars": 250,
"preview": "/node_modules\r\n/.vscode\r\n/.vs\r\n/.git\r\n/obj\r\n/temp\r\n/bin\r\n/src\r\n/dist/tests\r\n/setup\r\n/scripts\r\n/lib\r\n*.js.map\r\n*.v12.suo\r"
},
{
"path": "packages/ts-nameof.macro/LICENSE",
"chars": 1080,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2019 David Sherret\n\nPermission is hereby granted, free of charge, to any person obt"
},
{
"path": "packages/ts-nameof.macro/README.md",
"chars": 1392,
"preview": "# ts-nameof.macro\n\n[](https://badge.fury.io/js/ts-nameof.ma"
},
{
"path": "packages/ts-nameof.macro/package.json",
"chars": 1600,
"preview": "{\n \"name\": \"ts-nameof.macro\",\n \"version\": \"4.2.2\",\n \"description\": \"Babel macro for nameof in TypeScript.\",\n \"main\":"
},
{
"path": "packages/ts-nameof.macro/scripts/common/createProject.ts",
"chars": 133,
"preview": "import { Project } from \"ts-morph\";\n\nexport function getProject() {\n return new Project({ tsConfigFilePath: \"tsconfig"
},
{
"path": "packages/ts-nameof.macro/scripts/common/index.ts",
"chars": 33,
"preview": "export * from \"./createProject\";\n"
},
{
"path": "packages/ts-nameof.macro/scripts/generation/createDeclarationFile.ts",
"chars": 777,
"preview": "import { ModuleDeclarationKind, Node, Project } from \"ts-morph\";\n\nexport function createDeclarationFile(project: Project"
},
{
"path": "packages/ts-nameof.macro/scripts/generation/main.ts",
"chars": 421,
"preview": "import { ArgsChecker } from \"@ts-nameof/scripts-common\";\nimport { getProject } from \"../common\";\nimport { createDeclarat"
},
{
"path": "packages/ts-nameof.macro/scripts/tsconfig.json",
"chars": 162,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es6\",\n \"noEmit\": true\n },\n \"extends\": \"../../../tsconfig.common.json\",\n \"e"
},
{
"path": "packages/ts-nameof.macro/src/index.js",
"chars": 1127,
"preview": "/// @ts-check\n/// <reference path=\"references.d.ts\" />\nimport { transformNode } from \"@ts-nameof/transforms-babel\";\nimpo"
},
{
"path": "packages/ts-nameof.macro/src/references.d.ts",
"chars": 38,
"preview": "declare module \"babel-plugin-macros\";\n"
},
{
"path": "packages/ts-nameof.macro/src/tests/macroTests.ts",
"chars": 958,
"preview": "/// <reference path=\"../references.d.ts\" />\nimport * as babel from \"@babel/core\";\nimport \"@babel/preset-typescript\";\nimp"
},
{
"path": "packages/ts-nameof.macro/src/tests/ts-nameof.macro/index.js",
"chars": 82,
"preview": "/// @ts-check\n// hack to get tests working\nexport { default } from \"../../index\";\n"
},
{
"path": "packages/ts-nameof.macro/ts-nameof.macro.d.ts",
"chars": 6025,
"preview": "declare module \"ts-nameof.macro\" {\n /**\n * Gets a string representation of the final identifier of the given expressi"
},
{
"path": "packages/ts-nameof.macro/tsconfig.json",
"chars": 275,
"preview": "{\n \"compilerOptions\": {\n \"allowJs\": true,\n \"outDir\": \"./dist\"\n },\n \"extends\": \"../../tsconfig.common.json\",\n \""
},
{
"path": "tsconfig.common.json",
"chars": 384,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2015\",\n \"module\": \"commonjs\",\n \"declaration\": false,\n \"sourceMap\": tr"
}
]
About this extraction
This page contains the full source code of the dsherret/ts-nameof GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 129 files (163.2 KB), approximately 44.8k tokens, and a symbol index with 104 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.