diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7fcc87a6..2e8b31d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [18.x, 20.x, 22.x] + node: [20.x, 22.x, 24.x] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 diff --git a/.prettierignore b/.prettierignore index 38f3a602..68881e4d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,6 @@ tests/ lib/ +examples/ +.github/ +docs/ +*.md \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..39c173f4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,16 @@ +{ + "printWidth": 80, + "tabWidth": 4, + "trailingComma": "all", + "singleQuote": true, + "bracketSameLine": true, + "semi": true, + "importOrder": ["^[./]"], + "importOrderSeparation": true, + "importOrderSortSpecifiers": true, + "plugins": [ + "prettier-plugin-ember-template-tag", + "prettier-plugin-svelte", + "./lib/src/index.js" + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b7b71c6..7d566c00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ --- +### 6.0.0 + +#### Breaking Changes +- **Switch to ESM** [#366](https://github.com/trivago/prettier-plugin-sort-imports/pull/366) by [@RobbieTheWagner](https://github.com/RobbieTheWagner) - The plugin now uses ES modules instead of CommonJS +- **Require Node >= 20.x** [#367](https://github.com/trivago/prettier-plugin-sort-imports/pull/367) by [@RobbieTheWagner](https://github.com/RobbieTheWagner) - Drop support for Node.js versions below 20 +- **Switch to vitest** [#370](https://github.com/trivago/prettier-plugin-sort-imports/pull/370) by [@RobbieTheWagner](https://github.com/RobbieTheWagner) - Replaced Jest with Vitest for better ESM support + +#### New features +- **Ember.js gjs/gts support** [#377](https://github.com/trivago/prettier-plugin-sort-imports/pull/377) by [@NullVoxPopuli](https://github.com/NullVoxPopuli) and [@RobbieTheWagner](https://github.com/RobbieTheWagner) - Add support for Ember.js gjs/gts file formats with namespace and named type imports +- **`` placeholder** [#381](https://github.com/trivago/prettier-plugin-sort-imports/pull/381) by [@sdotson](https://github.com/sdotson) - Add support for sorting Node.js builtin modules to a specific position using `` placeholder in `importOrder` +- **`` placeholder** [#339](https://github.com/trivago/prettier-plugin-sort-imports/pull/339) - Add `` keyword for fine-grained control over import group separation when `importOrderSeparation` is enabled +- **Sort by length option** [#224](https://github.com/trivago/prettier-plugin-sort-imports/pull/224) by [@KLewin23](https://github.com/KLewin23) - Add option to sort imports by import statement length +- **`importOrderExclude` option** [#384](https://github.com/trivago/prettier-plugin-sort-imports/pull/384) by [@RyderKishan](https://github.com/RyderKishan) - Add support for excluding specific files from import sorting using glob patterns +- **Expand `sort-imports-ignore` detection** [#358](https://github.com/trivago/prettier-plugin-sort-imports/pull/358) by [@ckwalsh](https://github.com/ckwalsh) - Improved detection of `sort-imports-ignore` comments throughout the file, not just at line 1 + +#### Performance improvements +- **Improve `removeNodesFromOriginalCode()` performance** [#356](https://github.com/trivago/prettier-plugin-sort-imports/pull/356) by [@ckwalsh](https://github.com/ckwalsh) - Replace RegExp logic with string slices for better performance + +#### Refactoring +- **Stop rerendering directives** [#357](https://github.com/trivago/prettier-plugin-sort-imports/pull/357) by [@ckwalsh](https://github.com/ckwalsh) - Inject imports instead of re-rendering directives to better preserve whitespace and reduce formatting conflicts +- **Stop ignoring exceptions in snapshot tests** [#355](https://github.com/trivago/prettier-plugin-sort-imports/pull/355) by [@ckwalsh](https://github.com/ckwalsh) - Improve test reliability by properly handling exceptions + +#### Chores +- Add pnpm install command to README [#361](https://github.com/trivago/prettier-plugin-sort-imports/pull/361) - Document pnpm installation option + ### 5.2.2 - Update packages and pin babel/types [#343](https://github.com/trivago/prettier-plugin-sort-imports/pull/343) by [@byara](https://github.com/byara) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 3756dff5..ef7c3ea2 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,21 +14,21 @@ orientation. 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 +- 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 +- 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 +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities @@ -71,4 +71,4 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], versi available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ \ No newline at end of file +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/README.md b/README.md index 1fb0befd..6762e95e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ import React, { KeyboardEvent, } from 'react'; import { logger } from '@core/logger'; -import { reduce, debounce } from 'lodash'; +import { reduce, debounce } from 'lodash-es'; import { Message } from '../Message'; import { createServer } from '@server/node'; import { Alert } from '@ui/Alert'; @@ -29,7 +29,7 @@ import { createConnection } from '@server/database'; ### Output ```javascript -import { debounce, reduce } from 'lodash'; +import { debounce, reduce } from 'lodash-es'; import React, { ChangeEvent, FC, @@ -53,18 +53,24 @@ import { add, filter, repeat } from '../utils'; ### Install -npm +using npm ```shell script npm install --save-dev @trivago/prettier-plugin-sort-imports ``` -or, using yarn +using yarn ```shell script yarn add --dev @trivago/prettier-plugin-sort-imports ``` +using pnpm + +```shell script +pnpm add -D @trivago/prettier-plugin-sort-imports +``` + **Note: If you are migrating from v2.x.x to v3.x.x, [Please Read Migration Guidelines](./docs/MIGRATION.md)** **Note: If formatting `.vue` sfc files please install `@vue/compiler-sfc` if not in your dependency tree - this normally is within Vue projects.** @@ -114,6 +120,14 @@ To move the third party imports at desired place, you can use `", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"], ``` +You can also use `` to control the position of Node.js builtin modules (like `fs`, `path`, `http`, and their `node:` prefixed variants): + +``` +"importOrder": ["", "", "^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"], +``` + +When `` is included in your `importOrder`, Node.js builtin modules will be sorted to that position. If not included, builtin modules are treated as regular third-party imports. + #### `importOrderSeparation` **type**: `boolean` @@ -127,6 +141,9 @@ between sorted import declarations group. The separation takes place according t "importOrderSeparation": true, ``` +If this option is enabled and `` is used in the `importOrder` array, the plugin +will ONLY add newlines at those locations and at the end of the imports. + #### `importOrderSortSpecifiers` **type**: `boolean` @@ -198,6 +215,13 @@ with options as a JSON string of the plugin array: importOrderParserPlugins: [] ``` +### `importOrderSortByLength` +**type**: `'asc' | 'desc' | null` +**default value**: `null` + +A choice value to enable sorting imports within their groups based on their string lengths, the two options being ascending and descending. +Leaving the value blank or setting it to null will result in length being ignored + ### `importOrderSideEffects` **type**: `boolean` **default value**: `true` @@ -229,6 +253,7 @@ import b from 'b' import c from 'c' ``` + ### Ignoring import ordering In some cases it's desired to ignore import ordering, specifically if you require to instantiate a common service or polyfill in your application logic before all the other imports. The plugin supports the `// sort-imports-ignore` comment, which will exclude the file from ordering the imports. @@ -267,6 +292,12 @@ In the end, the plugin returns final imports with _third party imports_ on top a The _third party imports_ position (it's top by default) can be overridden using the `` special word in the `importOrder`. +### Pattern Matching Implementation + +This plugin uses [minimatch](https://github.com/isaacs/minimatch) for pattern matching of import paths. The matching is performed using the exact version specified in the plugin's dependencies to ensure consistent behavior. This is important to note because different versions of minimatch or other glob matching libraries might have subtle differences in their pattern matching behavior. + +If you're experiencing unexpected matching behavior, please ensure you're using patterns compatible with minimatch's syntax, which might differ slightly from other glob implementations. + ### FAQ / Troubleshooting Having some trouble or an issue ? You can check [FAQ / Troubleshooting section](./docs/TROUBLESHOOTING.md). @@ -277,11 +308,13 @@ Having some trouble or an issue ? You can check [FAQ / Troubleshooting section]( | ---------------------- | ------------------------ | ------------------------------------------------ | | JS with ES Modules | ✅ Everything | - | | NodeJS with ES Modules | ✅ Everything | - | +| Angular | ✅ Everything | Supported through `importOrderParserPlugins` API | +| Ember | ✅ Everything | `prettier-plugin-ember-template-tag` is required | | React | ✅ Everything | - | | Solid | ✅ Everything | - | -| Angular | ✅ Everything | Supported through `importOrderParserPlugins` API | -| Vue | ✅ Everything | `@vue/compiler-sfc` is required | | Svelte | ✅ Everything | `prettier-plugin-svelte` is required | +| Vue | ✅ Everything | `@vue/compiler-sfc` is required | + ### Used by @@ -299,10 +332,10 @@ debug some code in the plugin, check [Debugging Guidelines](./docs/DEBUG.md) ### Maintainers -| [Ayush Sharma](https://github.com/ayusharma) | [Behrang Yarahmadi](https://github.com/byara) | [Vladislav Arsenev](https://github.com/vladislavarsenev) | -| ------------------------------------------------------------------------ | --------------------------------------------------------------------- |--------------------------------------------------------------------------| -| ![ayusharma](https://avatars2.githubusercontent.com/u/6918450?s=120&v=4) | ![@byara](https://avatars2.githubusercontent.com/u/6979966?s=120&v=4) |![@vladislavarsenev](https://avatars.githubusercontent.com/u/51095682?s=120&v=4)| -| [@ayusharma](https://twitter.com/ayusharma_) | [@behrang_y](https://twitter.com/behrang_y) | | +| [Ayush Sharma](https://github.com/ayusharma) | [Behrang Yarahmadi](https://github.com/byara) | +| ------------------------------------------------------------------------ | --------------------------------------------------------------------- | +| ![ayusharma](https://avatars2.githubusercontent.com/u/6918450?s=120&v=4) | ![@byara](https://avatars2.githubusercontent.com/u/6979966?s=120&v=4) | +| [@ayusharma\_](https://twitter.com/ayusharma_) | [@behrang_y](https://twitter.com/behrang_y) | ### Disclaimer diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index 3aadcd8a..9b9b3ffa 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -87,7 +87,7 @@ Due to the package handling of the pnpm, sometimes, the plugin is not automatica via prettier config. ```js module.exports = { - plugins: [require('@trivago/prettier-plugin-sort-imports')], + plugins: ['@trivago/prettier-plugin-sort-imports'], } ``` diff --git a/examples/.prettierrc b/examples/.prettierrc index b40f64a2..a0118299 100644 --- a/examples/.prettierrc +++ b/examples/.prettierrc @@ -8,5 +8,9 @@ "importOrder": ["^@server/(.*)$", "^@core/(.*)$", "^@ui/(.*)$", "^[./]"], "importOrderSeparation": true, "importOrderSortSpecifiers": true, - "plugins": ["../lib/src/index.js"] + "plugins": [ + "prettier-plugin-ember-template-tag", + "prettier-plugin-svelte", + "../lib/src/index.js" + ] } diff --git a/examples/example.gjs b/examples/example.gjs new file mode 100644 index 00000000..1f23d3f2 --- /dev/null +++ b/examples/example.gjs @@ -0,0 +1,29 @@ +// I am top level comment in this file. +import z from 'z'; +import threeLevelRelativePath from '../../../threeLevelRelativePath'; +import sameLevelRelativePath from './sameLevelRelativePath'; +import thirdParty from 'third-party'; +import oneLevelRelativePath from '../oneLevelRelativePath'; +import otherthing from '@core/otherthing'; +import abc from '@core/abc'; +import twoLevelRelativePath from '../../twoLevelRelativePath'; +import component from '@ui/hello'; +import fourLevelRelativePath from '../../../../fourLevelRelativePath'; +import * as a from 'a'; +import something from '@server/something'; +import xyz from '@ui/xyz'; +import Component from '@glimmer/component'; +import { action } from '@ember/object'; + +const what = ; + +export const who = ; + +export default class Foo extends Component { + @action + myCoolFunction(){} + + +} \ No newline at end of file diff --git a/examples/example.gts b/examples/example.gts new file mode 100644 index 00000000..b5bb46d5 --- /dev/null +++ b/examples/example.gts @@ -0,0 +1,41 @@ +// I am top level comment in this file. +import z from 'z'; +import threeLevelRelativePath from '../../../threeLevelRelativePath'; +import sameLevelRelativePath from './sameLevelRelativePath'; +import thirdParty from 'third-party'; +import oneLevelRelativePath from '../oneLevelRelativePath'; +import otherthing from '@core/otherthing'; +import abc from '@core/abc'; +import twoLevelRelativePath from '../../twoLevelRelativePath'; +import component from '@ui/hello'; +import fourLevelRelativePath from '../../../../fourLevelRelativePath'; +import * as a from 'a'; +import type RouterService from '@ember/routing/router-service'; +import something from '@server/something'; +import xyz from '@ui/xyz'; +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { getOwner } from '@ember/owner'; +import { setOwner } from '@ember/owner'; +import type Owner from '@ember/owner'; +import type DefaultOwner from '@ember/owner'; +import type * as AllOwner from '@ember/owner'; + +interface FooSignature { + Args: { + bar: string + }; +} + +const what = ; + +export const who = ; + +export default class Foo extends Component { + @action + myCoolFunction(){} + + +} diff --git a/examples/example.ts b/examples/example.ts index f9a69c4c..9bcb69c4 100644 --- a/examples/example.ts +++ b/examples/example.ts @@ -6,7 +6,7 @@ import React, { KeyboardEvent, } from 'react'; import { logger } from '@core/logger'; -import { reduce, debounce } from 'lodash'; +import { reduce, debounce } from 'lodash-es'; import { Message } from '../Message'; import { createServer } from '@server/node'; import { Alert } from '@ui/Alert'; diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index a2335d3e..00000000 --- a/jest.config.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -const ENABLE_COVERAGE = false; // !!process.env.CI; - -module.exports = { - displayName: 'test', - setupFiles: ['/test-setup/run_spec.js'], - snapshotSerializers: ['/test-setup/raw-serializer.js'], - testRegex: 'ppsi\\.spec\\.js$|__tests__/.*\\.ts$', - collectCoverage: ENABLE_COVERAGE, - collectCoverageFrom: ['src/**/*.ts', '!/node_modules/'], - preset: 'ts-jest', - testEnvironment: 'node', -}; diff --git a/package.json b/package.json index 9ba8fac2..06f16194 100644 --- a/package.json +++ b/package.json @@ -1,81 +1,87 @@ { - "name": "@trivago/prettier-plugin-sort-imports", - "version": "5.2.2", - "description": "A prettier plugins to sort imports in provided RegEx order", - "main": "lib/src/index.js", - "types": "types/index.d.ts", - "repository": { - "url": "https://github.com/trivago/prettier-plugin-sort-imports", - "type": "git" - }, - "homepage": "https://github.com/trivago/prettier-plugin-sort-imports#readme", - "scripts": { - "prepare": "yarn run compile", - "compile": "tsc", - "preexample": "yarn run compile", - "example": "prettier --config ./examples/.prettierrc --plugin lib/src/index.js", - "test": "yarn node --experimental-vm-modules $(yarn bin jest)", - "type-check": "tsc --noEmit", - "prepublishOnly": "npm run compile && npm run test" - }, - "keywords": [ - "prettier", - "plugin", - "sort", - "import", - "typescript", - "javascript" - ], - "author": { - "name": "Ayush Sharma", - "email": "ayush.sharma@trivago.com", - "url": "https://github.com/ayusharma" - }, - "license": "Apache-2.0", - "dependencies": { - "@babel/generator": "^7.26.5", - "@babel/parser": "^7.26.7", - "@babel/traverse": "^7.26.7", - "@babel/types": "^7.26.7", - "javascript-natural-sort": "^0.7.1", - "lodash": "^4.17.21" - }, - "devDependencies": { - "@babel/core": "^7.26.7", - "@types/chai": "^5.0.1", - "@types/jest": "^29.5.14", - "@types/lodash": "^4.17.14", - "@types/node": "^22.10.10", - "@vue/compiler-sfc": "^3.5.13", - "jest": "^29.7.0", - "prettier": "^3.4.2", - "prettier-plugin-svelte": "^3.3.3", - "svelte": "^4.2.19", - "ts-jest": "^29.2.5", - "typescript": "^5.7.3" - }, - "peerDependencies": { - "@vue/compiler-sfc": "3.x", - "prettier": "2.x - 3.x", - "prettier-plugin-svelte": "3.x", - "svelte": "4.x || 5.x" - }, - "engines": { - "node": ">18.12" - }, - "peerDependenciesMeta": { - "@vue/compiler-sfc": { - "optional": true + "name": "@trivago/prettier-plugin-sort-imports", + "version": "6.0.0-0", + "description": "A prettier plugins to sort imports in provided RegEx order", + "main": "lib/src/index.js", + "type": "module", + "types": "types/index.d.ts", + "repository": { + "url": "https://github.com/trivago/prettier-plugin-sort-imports", + "type": "git" }, - "prettier-plugin-svelte": { - "optional": true + "homepage": "https://github.com/trivago/prettier-plugin-sort-imports#readme", + "scripts": { + "prepare": "yarn run compile", + "compile": "tsc", + "preexample": "yarn run compile", + "example": "prettier --config ./examples/.prettierrc", + "test": "vitest run", + "type-check": "tsc --noEmit", + "prepublishOnly": "npm run compile && npm run test", + "prettify": "yarn prettier --write ." }, - "svelte": { - "optional": true + "keywords": [ + "prettier", + "plugin", + "sort", + "import", + "typescript", + "javascript" + ], + "author": { + "name": "Ayush Sharma", + "email": "ayush.sharma@trivago.com", + "url": "https://github.com/ayusharma" + }, + "license": "Apache-2.0", + "dependencies": { + "@babel/generator": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", + "javascript-natural-sort": "^0.7.1", + "lodash-es": "^4.17.21", + "parse-imports-exports": "^0.2.4", + "minimatch": "^9.0.0" + }, + "devDependencies": { + "@babel/core": "^7.26.7", + "@types/babel__core": "^7.20.5", + "@types/babel__generator": "^7.27.0", + "@types/babel__traverse": "^7.20.7", + "@types/lodash-es": "^4.17.12", + "@types/minimatch": "^5.1.2", + "@types/node": "^22.10.10", + "@vue/compiler-sfc": "^3.5.13", + "prettier": "^3.4.2", + "prettier-plugin-ember-template-tag": "^2.1.0", + "prettier-plugin-svelte": "^3.3.3", + "svelte": "^4.2.19", + "typescript": "^5.7.3", + "vitest": "^3.2.4" + }, + "peerDependencies": { + "@vue/compiler-sfc": "3.x", + "prettier": "2.x - 3.x", + "prettier-plugin-ember-template-tag": ">= 2.0.0", + "prettier-plugin-svelte": "3.x", + "svelte": "4.x || 5.x" + }, + "engines": { + "node": ">= 20" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + }, + "prettier-plugin-ember-template-tag": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + }, + "svelte": { + "optional": true + } } - }, - "resolutions": { - "@types/babel__generator": "7.6.8", - "@babel/types": "7.26.7" - } } diff --git a/prettier.config.js b/prettier.config.js deleted file mode 100644 index da3932aa..00000000 --- a/prettier.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - printWidth: 80, - tabWidth: 4, - trailingComma: 'all', - singleQuote: true, - jsxBracketSameLine: true, - semi: true, - plugins: [require('./lib/src/index.js')], - importOrder: ['^[./]'], - importOrderSeparation: true, - importOrderSortSpecifiers: true, -}; diff --git a/src/constants.ts b/src/constants.ts index 28c009e7..a0c2c688 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -18,10 +18,13 @@ export const chunkSideOtherNode = 'other-node'; */ export const THIRD_PARTY_MODULES_SPECIAL_WORD = ''; +export const BUILTIN_MODULES_SPECIAL_WORD = ''; + export const THIRD_PARTY_TYPES_SPECIAL_WORD = ''; export const TYPES_SPECIAL_WORD = ''; +export const SEPARATOR_SPECIAL_WORD = ''; -const PRETTIER_PLUGIN_SORT_IMPORTS_NEW_LINE = +export const PRETTIER_PLUGIN_SORT_IMPORTS_NEW_LINE = 'PRETTIER_PLUGIN_SORT_IMPORTS_NEW_LINE'; export const newLineNode = expressionStatement( diff --git a/src/index.ts b/src/index.ts index 216f609d..b449bfd2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,28 @@ +import type { Options } from 'prettier'; import { parsers as babelParsers } from 'prettier/plugins/babel'; import { parsers as flowParsers } from 'prettier/plugins/flow'; import { parsers as htmlParsers } from 'prettier/plugins/html'; import { parsers as typescriptParsers } from 'prettier/plugins/typescript'; -import { defaultPreprocessor } from './preprocessors/default-processor'; -import { sveltePreprocessor } from './preprocessors/svelte-preprocessor'; -import { vuePreprocessor } from './preprocessors/vue-preprocessor'; -import type { Options } from 'prettier'; -import { createSvelteParsers } from './utils/create-svelte-parsers'; +import { defaultPreprocessor } from './preprocessors/default-processor.js'; +import { emberPreprocessor } from './preprocessors/ember-preprocessor.js'; +import { sveltePreprocessor } from './preprocessors/svelte-preprocessor.js'; +import { vuePreprocessor } from './preprocessors/vue-preprocessor.js'; +import { createEmberParsers } from './utils/create-ember-parsers.js'; +import { createSvelteParsers } from './utils/create-svelte-parsers.js'; -const svelteParsers = createSvelteParsers(); +const emberParsers = await createEmberParsers(); +const svelteParsers = await createSvelteParsers(); const options: Options = { + importOrderExclude: { + type: 'path', + category: 'Global', + array: true, + default: [{ value: [] }], + description: + 'Provide a list of glob patterns to exclude from import sorting.', + }, importOrder: { type: 'path', category: 'Global', @@ -53,6 +64,23 @@ const options: Options = { default: false, description: 'Should specifiers be sorted?', }, + importOrderSortByLength: { + type: 'choice', + category: 'Global', + default: null, + choices: [ + { value: 'asc', description: 'will sort from shortest to longest' }, + { + value: 'desc', + description: 'will sort from longest to shortest', + }, + { + value: null, + description: 'will disable sorting based on length', + }, + ], + description: 'Should imports be sorted by their string length', + }, importOrderSideEffects: { type: 'boolean', category: 'Global', @@ -67,7 +95,7 @@ const options: Options = { }, }; -module.exports = { +export default { parsers: { babel: { ...babelParsers.babel, @@ -93,6 +121,14 @@ module.exports = { }, } : {}), + ...(emberParsers.parsers + ? { + 'ember-template-tag': { + ...emberParsers.parsers['ember-template-tag'], + preprocess: emberPreprocessor, + }, + } + : {}), }, options, }; diff --git a/src/preprocessors/default-processor.ts b/src/preprocessors/default-processor.ts index e5dd8c39..9cd5791d 100644 --- a/src/preprocessors/default-processor.ts +++ b/src/preprocessors/default-processor.ts @@ -1,5 +1,5 @@ import { PrettierOptions } from '../types'; -import { preprocessor } from './preprocessor'; +import { preprocessor } from './preprocessor.js'; export function defaultPreprocessor(code: string, options: PrettierOptions) { for (const extension of ['svelte', 'vue']) { diff --git a/src/preprocessors/ember-preprocessor.ts b/src/preprocessors/ember-preprocessor.ts new file mode 100644 index 00000000..9fb8ebff --- /dev/null +++ b/src/preprocessors/ember-preprocessor.ts @@ -0,0 +1,52 @@ +import { parseImportsExports } from 'parse-imports-exports'; + +import { PrettierOptions } from '../types'; +import { replaceAt } from '../utils/replace-at.js'; +import { preprocessor } from './preprocessor.js'; + +const sortImports = (code: string, options: PrettierOptions) => { + const importsExports = parseImportsExports(code, { + ignoreDynamicImports: true, + ignoreRegexpLiterals: true, + ignoreRequires: true, + ignoreCommonJsExports: true, + }); + + let justImports = ''; + + function ingest(collection?: { + [key: string]: readonly { + readonly start: number; + readonly end: number; + }[]; + }) { + if (!collection) return; + + for (let [, info] of Object.entries({ ...collection })) { + for (let pos of info) { + justImports += code.slice(pos.start, pos.end + 1); + + let spaces = ''; + for (let i = 0; i < pos.end - pos.start; i++) { + spaces += ' '; + } + + code = replaceAt(code, pos.start, spaces); + } + } + } + + ingest(importsExports.namedImports); + ingest(importsExports.namespaceImports); + ingest(importsExports.typeNamedImports); + ingest(importsExports.typeNamespaceImports); + + let output = preprocessor(justImports, options); + let result = output + code; + + return result; +}; + +export function emberPreprocessor(code: string, options: PrettierOptions) { + return sortImports(code, options); +} diff --git a/src/preprocessors/preprocessor.ts b/src/preprocessors/preprocessor.ts index 716212ec..21a71e84 100644 --- a/src/preprocessors/preprocessor.ts +++ b/src/preprocessors/preprocessor.ts @@ -1,12 +1,14 @@ import { ParserOptions, parse as babelParser } from '@babel/parser'; -import { Directive, ImportDeclaration } from '@babel/types'; +import { ImportDeclaration } from '@babel/types'; import { PrettierOptions } from '../types'; -import { extractASTNodes } from '../utils/extract-ast-nodes'; -import { getCodeFromAst } from '../utils/get-code-from-ast'; -import { getExperimentalParserPlugins } from '../utils/get-experimental-parser-plugins'; -import { getSortedNodes } from '../utils/get-sorted-nodes'; -import { isSortImportsIgnored } from '../utils/is-sort-imports-ignored'; +import { extractASTNodes } from '../utils/extract-ast-nodes.js'; +import { getAllCommentsFromNodes } from '../utils/get-all-comments-from-nodes.js'; +import { getCodeFromAst } from '../utils/get-code-from-ast.js'; +import { getExperimentalParserPlugins } from '../utils/get-experimental-parser-plugins.js'; +import { getSortedNodes } from '../utils/get-sorted-nodes.js'; +import { isSortImportsIgnored } from '../utils/is-sort-imports-ignored.js'; +import { shouldSkipFile } from '../utils/should-skip-file.js'; export function preprocessor(code: string, options: PrettierOptions) { const { @@ -16,27 +18,40 @@ export function preprocessor(code: string, options: PrettierOptions) { importOrderSeparation, importOrderGroupNamespaceSpecifiers, importOrderSortSpecifiers, + importOrderSortByLength, importOrderSideEffects, importOrderImportAttributesKeyword, + importOrderExclude, + filepath, } = options; + // Check if the file should be skipped + if ( + filepath && + shouldSkipFile(filepath, (importOrderExclude || []) as string[]) + ) { + return code; + } + const parserOptions: ParserOptions = { sourceType: 'module', plugins: getExperimentalParserPlugins(importOrderParserPlugins), }; const ast = babelParser(code, parserOptions); - const interpreter = ast.program.interpreter; + + if (isSortImportsIgnored(ast.program.body[0]?.leadingComments ?? [])) + return code; const { importNodes, - directives, - }: { importNodes: ImportDeclaration[]; directives: Directive[] } = + injectIdx, + }: { importNodes: ImportDeclaration[]; injectIdx: number } = extractASTNodes(ast); // short-circuit if there are no import declaration if (importNodes.length === 0) return code; - if (isSortImportsIgnored(importNodes)) return code; + if (isSortImportsIgnored(getAllCommentsFromNodes(importNodes))) return code; const allImports = getSortedNodes(importNodes, { importOrder, @@ -44,10 +59,11 @@ export function preprocessor(code: string, options: PrettierOptions) { importOrderSeparation, importOrderGroupNamespaceSpecifiers, importOrderSortSpecifiers, + importOrderSortByLength, importOrderSideEffects, }); - return getCodeFromAst(allImports, directives, code, interpreter, { + return getCodeFromAst(allImports, code, injectIdx, { importOrderImportAttributesKeyword, }); } diff --git a/src/preprocessors/svelte-preprocessor.ts b/src/preprocessors/svelte-preprocessor.ts index 6d1ab285..4c317cf3 100644 --- a/src/preprocessors/svelte-preprocessor.ts +++ b/src/preprocessors/svelte-preprocessor.ts @@ -1,14 +1,31 @@ import { PrettierOptions } from '../types'; -import { preprocessor } from './preprocessor'; +import { preprocessor } from './preprocessor.js'; + +let prettierPluginSvelte: typeof import('prettier-plugin-svelte') | undefined; +let svelteCompiler: typeof import('svelte/compiler') | undefined; + +try { + prettierPluginSvelte = await import('prettier-plugin-svelte'); + svelteCompiler = await import('svelte/compiler'); +} catch { + // Do not error because the dependency is optional. +} const booleanGuard = (value: T | undefined): value is T => Boolean(value); const sortImports = (code: string, options: PrettierOptions) => { - const { parse } = require('svelte/compiler'); + if (!svelteCompiler) { + throw new Error( + "Missing peer dependency 'svelte/compiler'. Please install it to use the svelte parser.", + ); + } + + const { parse } = svelteCompiler; const { instance, module } = parse(code); const sources = [instance, module].filter(booleanGuard); if (!sources.length) return code; return sources.reduce((code, source) => { + // @ts-expect-error TODO: Fix this type error const snippet = code.slice(source.content.start, source.content.end); const preprocessed = preprocessor(snippet, options); const result = code.replace(snippet, `\n${preprocessed}\n`); @@ -19,6 +36,11 @@ const sortImports = (code: string, options: PrettierOptions) => { export function sveltePreprocessor(code: string, options: PrettierOptions) { const sorted = sortImports(code, options); - const prettierPluginSvelte = require('prettier-plugin-svelte'); + if (!prettierPluginSvelte) { + throw new Error( + "Missing peer dependency 'prettier-plugin-svelte'. Please install it to use the svelte parser.", + ); + } + // @ts-expect-error TODO: Fix this type error return prettierPluginSvelte.parsers.svelte.preprocess(sorted, options); } diff --git a/src/preprocessors/vue-preprocessor.ts b/src/preprocessors/vue-preprocessor.ts index 66b2bca4..1e2074bd 100644 --- a/src/preprocessors/vue-preprocessor.ts +++ b/src/preprocessors/vue-preprocessor.ts @@ -1,8 +1,22 @@ import { PrettierOptions } from '../types'; -import { preprocessor } from './preprocessor'; +import { preprocessor } from './preprocessor.js'; + +let vueCompilerSfc: typeof import('@vue/compiler-sfc') | undefined; + +try { + vueCompilerSfc = await import('@vue/compiler-sfc'); +} catch { + // Do not error because the dependency is optional. +} export function vuePreprocessor(code: string, options: PrettierOptions) { - const { parse } = require('@vue/compiler-sfc'); + if (!vueCompilerSfc) { + throw new Error( + "Missing peer dependency '@vue/compiler-sfc'. Please install it to use the vue parser.", + ); + } + + const { parse } = vueCompilerSfc; const { descriptor } = parse(code); const scriptContent = descriptor.script?.content; diff --git a/src/types.ts b/src/types.ts index 085c0b15..799e4e00 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,6 +19,7 @@ export type GetSortedNodes = ( | 'importOrderSeparation' | 'importOrderGroupNamespaceSpecifiers' | 'importOrderSortSpecifiers' + | 'importOrderSortByLength' | 'importOrderSideEffects' >, ) => ImportOrLine[]; diff --git a/src/utils/__tests__/adjust-comments-on-sorted-nodes.spec.ts b/src/utils/__tests__/adjust-comments-on-sorted-nodes.spec.ts index acf76c09..1a0edc6b 100644 --- a/src/utils/__tests__/adjust-comments-on-sorted-nodes.spec.ts +++ b/src/utils/__tests__/adjust-comments-on-sorted-nodes.spec.ts @@ -1,4 +1,5 @@ import { ImportDeclaration } from '@babel/types'; +import { expect, test } from 'vitest'; import { adjustCommentsOnSortedNodes } from '../adjust-comments-on-sorted-nodes'; import { getImportNodes } from '../get-import-nodes'; diff --git a/src/utils/__tests__/assemble-updated-code.spec.ts b/src/utils/__tests__/assemble-updated-code.spec.ts new file mode 100644 index 00000000..76204867 --- /dev/null +++ b/src/utils/__tests__/assemble-updated-code.spec.ts @@ -0,0 +1,79 @@ +import { format } from 'prettier'; +import { expect, test } from 'vitest'; + +import { assembleUpdatedCode } from '../assemble-updated-code'; +import { getAllCommentsFromNodes } from '../get-all-comments-from-nodes'; +import { getImportNodes } from '../get-import-nodes'; +import { getSortedNodes } from '../get-sorted-nodes'; + +const code = `"use strict"; +// first comment +// second comment +import z from 'z'; +import c from 'c'; +import g from 'g'; +import t from 't'; +import k from 'k'; +// import a from 'a'; + // import a from 'a'; +import a from 'a'; +`; + +test('it should remove nodes from the original code', async () => { + const importNodes = getImportNodes(code); + const sortedNodes = getSortedNodes(importNodes, { + importOrder: [], + importOrderCaseInsensitive: false, + importOrderSeparation: false, + importOrderGroupNamespaceSpecifiers: false, + importOrderSortSpecifiers: false, + importOrderSideEffects: true, + importOrderSortByLength: null, + }); + const allCommentsFromImports = getAllCommentsFromNodes(sortedNodes); + + const commentAndImportsToRemoveFromCode = [ + ...sortedNodes, + ...allCommentsFromImports, + ]; + const codeWithoutImportDeclarations = assembleUpdatedCode( + code, + commentAndImportsToRemoveFromCode, + ); + const result = await format(codeWithoutImportDeclarations, { + parser: 'babel', + }); + expect(result).toEqual(`"use strict"; +`); +}); + +test('it should inject the generated code at the correct location', async () => { + const importNodes = getImportNodes(code); + const sortedNodes = getSortedNodes(importNodes, { + importOrder: [], + importOrderCaseInsensitive: false, + importOrderSeparation: false, + importOrderGroupNamespaceSpecifiers: false, + importOrderSortSpecifiers: false, + importOrderSideEffects: true, + importOrderSortByLength: null, + }); + const allCommentsFromImports = getAllCommentsFromNodes(sortedNodes); + + const commentAndImportsToRemoveFromCode = [ + ...sortedNodes, + ...allCommentsFromImports, + ]; + const codeWithoutImportDeclarations = assembleUpdatedCode( + code, + commentAndImportsToRemoveFromCode, + `import generated from "generated";`, + '"use strict";'.length, + ); + const result = await format(codeWithoutImportDeclarations, { + parser: 'babel', + }); + expect(result).toEqual(`"use strict"; +import generated from "generated"; +`); +}); diff --git a/src/utils/__tests__/get-all-comments-from-nodes.spec.ts b/src/utils/__tests__/get-all-comments-from-nodes.spec.ts index c076e927..4455d55c 100644 --- a/src/utils/__tests__/get-all-comments-from-nodes.spec.ts +++ b/src/utils/__tests__/get-all-comments-from-nodes.spec.ts @@ -1,5 +1,6 @@ import { ParserOptions } from '@babel/parser'; import { CommentBlock, CommentLine, ImportDeclaration } from '@babel/types'; +import { expect, test } from 'vitest'; import { getAllCommentsFromNodes } from '../get-all-comments-from-nodes'; import { getImportNodes } from '../get-import-nodes'; @@ -14,6 +15,7 @@ const getSortedImportNodes = (code: string, options?: ParserOptions) => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null, importOrderSideEffects: true, }); }; diff --git a/src/utils/__tests__/get-code-from-ast.spec.ts b/src/utils/__tests__/get-code-from-ast.spec.ts index 298e9640..7c238b53 100644 --- a/src/utils/__tests__/get-code-from-ast.spec.ts +++ b/src/utils/__tests__/get-code-from-ast.spec.ts @@ -1,10 +1,7 @@ -import { parse as babelParser } from '@babel/core'; -import { ParserOptions } from '@babel/parser'; import { format } from 'prettier'; +import { expect, test } from 'vitest'; -import { extractASTNodes } from '../extract-ast-nodes'; import { getCodeFromAst } from '../get-code-from-ast'; -import { getExperimentalParserPlugins } from '../get-experimental-parser-plugins'; import { getImportNodes } from '../get-import-nodes'; import { getSortedNodes } from '../get-sorted-nodes'; @@ -25,9 +22,10 @@ import a from 'a'; importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null, importOrderSideEffects: true, }); - const formatted = getCodeFromAst(sortedNodes, [], code, null); + const formatted = getCodeFromAst(sortedNodes, code); expect(await format(formatted, { parser: 'babel' })).toEqual( `// first comment // second comment @@ -40,29 +38,3 @@ import z from "z"; `, ); }); - -test('it renders directives correctly', async () => { - const code = ` - "use client"; -// first comment -import b from 'b'; -import a from 'a';`; - - const parserOptions: ParserOptions = { - sourceType: 'module', - plugins: getExperimentalParserPlugins([]), - }; - const ast = babelParser(code, parserOptions); - if (!ast) throw new Error('ast is null'); - const { directives, importNodes } = extractASTNodes(ast); - - const formatted = getCodeFromAst(importNodes, directives, code, null); - expect(await format(formatted, { parser: 'babel' })).toEqual( - `"use client"; - -// first comment -import b from "b"; -import a from "a"; -`, - ); -}); diff --git a/src/utils/__tests__/get-experimental-parser-plugins.spec.ts b/src/utils/__tests__/get-experimental-parser-plugins.spec.ts index d1c7d35e..d37c82a1 100644 --- a/src/utils/__tests__/get-experimental-parser-plugins.spec.ts +++ b/src/utils/__tests__/get-experimental-parser-plugins.spec.ts @@ -1,3 +1,5 @@ +import { expect, test } from 'vitest'; + import { getExperimentalParserPlugins } from '../get-experimental-parser-plugins'; test('it should return empty list', () => { diff --git a/src/utils/__tests__/get-import-nodes-matched-group.spec.ts b/src/utils/__tests__/get-import-nodes-matched-group.spec.ts index b585eb48..26a342c2 100644 --- a/src/utils/__tests__/get-import-nodes-matched-group.spec.ts +++ b/src/utils/__tests__/get-import-nodes-matched-group.spec.ts @@ -1,7 +1,7 @@ -import { THIRD_PARTY_MODULES_SPECIAL_WORD } from '../../constants'; -import { ImportGroups } from '../../types'; -import { getImportNodes } from '../get-import-nodes'; -import { getImportNodesMatchedGroup } from '../get-import-nodes-matched-group'; +import { expect, test } from 'vitest'; + +import { getImportNodesMatchedGroup } from '../get-import-nodes-matched-group.js'; +import { getImportNodes } from '../get-import-nodes.js'; const code = `// first comment // second comment @@ -49,14 +49,11 @@ import type { ExternalType } from 'external-type-module'; import type { InternalType } from './internal-type-module'; import { externalFn } from 'external-fn-module'; import { internalFn } from './internal-fn-module'; - ` - const importNodes = getImportNodes(code,{ + `; + const importNodes = getImportNodes(code, { plugins: ['typescript'], }); - const importOrder = [ - '^[^.].*', - '^[.].*', - ]; + const importOrder = ['^[^.].*', '^[.].*']; let matchedGroups: string[] = []; for (const importNode of importNodes) { @@ -66,12 +63,7 @@ import { internalFn } from './internal-fn-module'; ); matchedGroups.push(matchedGroup); } - expect(matchedGroups).toEqual([ - '^[^.].*', - '^[.].*', - '^[^.].*', - '^[.].*', - ]); + expect(matchedGroups).toEqual(['^[^.].*', '^[.].*', '^[^.].*', '^[.].*']); }); test('should return type imports as part of a type-specific group even if a matching non-type specific group precedes it', () => { @@ -80,15 +72,11 @@ import type { ExternalType } from 'external-type-module'; import type { InternalType } from './internal-type-module'; import { externalFn } from 'external-fn-module'; import { internalFn } from './internal-fn-module'; - ` + `; const importNodes = getImportNodes(code, { plugins: ['typescript'], }); - const importOrder = [ - '^[^.].*', - '^[.].*', - '^[.].*', - ]; + const importOrder = ['^[^.].*', '^[.].*', '^[.].*']; let matchedGroups: string[] = []; for (const importNode of importNodes) { diff --git a/src/utils/__tests__/get-sorted-import-specifiers.spec.ts b/src/utils/__tests__/get-sorted-import-specifiers.spec.ts index 40675f27..97d405cc 100644 --- a/src/utils/__tests__/get-sorted-import-specifiers.spec.ts +++ b/src/utils/__tests__/get-sorted-import-specifiers.spec.ts @@ -1,3 +1,5 @@ +import { expect, test } from 'vitest'; + import { getImportNodes } from '../get-import-nodes'; import { getSortedImportSpecifiers } from '../get-sorted-import-specifiers'; import { getSortedNodesModulesNames } from '../get-sorted-nodes-modules-names'; diff --git a/src/utils/__tests__/get-sorted-nodes-by-import-order.spec.ts b/src/utils/__tests__/get-sorted-nodes-by-import-order.spec.ts index cc861b61..ff9e8dd1 100644 --- a/src/utils/__tests__/get-sorted-nodes-by-import-order.spec.ts +++ b/src/utils/__tests__/get-sorted-nodes-by-import-order.spec.ts @@ -1,9 +1,12 @@ import { ImportDeclaration } from '@babel/types'; +import { expect, test } from 'vitest'; -import { getImportNodes } from '../get-import-nodes'; -import { getSortedNodes } from '../get-sorted-nodes'; -import { getSortedNodesModulesNames } from '../get-sorted-nodes-modules-names'; -import { getSortedNodesNames } from '../get-sorted-nodes-names'; +import { PRETTIER_PLUGIN_SORT_IMPORTS_NEW_LINE } from '../../constants.js'; +import { ImportOrLine } from '../../types'; +import { getImportNodes } from '../get-import-nodes.js'; +import { getSortedNodesModulesNames } from '../get-sorted-nodes-modules-names.js'; +import { getSortedNodesNames } from '../get-sorted-nodes-names.js'; +import { getSortedNodes } from '../get-sorted-nodes.js'; const code = `// first comment // second comment @@ -28,6 +31,7 @@ test('it returns all sorted nodes', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; @@ -73,6 +77,7 @@ test('it returns all sorted nodes case-insensitive', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; @@ -118,6 +123,7 @@ test('it returns all sorted nodes with sort order', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; @@ -163,6 +169,7 @@ test('it returns all sorted nodes with sort order case-insensitive', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -207,6 +214,7 @@ test('it returns all sorted import nodes with sorted import specifiers', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: true, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -251,6 +259,7 @@ test('it returns all sorted import nodes with sorted import specifiers with case importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: true, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -295,6 +304,7 @@ test('it returns all sorted nodes with custom third party modules', () => { importOrderCaseInsensitive: true, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -320,6 +330,7 @@ test('it returns all sorted nodes with namespace specifiers at the top', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: true, importOrderSortSpecifiers: false, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; @@ -337,3 +348,160 @@ test('it returns all sorted nodes with namespace specifiers at the top', () => { 'z', ]); }); + +test('it returns the default separations if `importOrderSeparation` is false', () => { + const result = getImportNodes(code); + const sorted = getSortedNodes(result, { + importOrder: [ + '', + '^a$', + '^t$', + '', + '^k$', + '^B', + '', + ], + importOrderCaseInsensitive: false, + importOrderSeparation: false, + importOrderGroupNamespaceSpecifiers: false, + importOrderSortSpecifiers: false, + importOrderSideEffects: true, + importOrderSortByLength: null, + }); + expect(getSeparationData(sorted)).toEqual([ + { type: 'ImportDeclaration', value: 'XY' }, + { type: 'ImportDeclaration', value: 'Xa' }, + { type: 'ImportDeclaration', value: 'c' }, + { type: 'ImportDeclaration', value: 'g' }, + { type: 'ImportDeclaration', value: 'x' }, + { type: 'ImportDeclaration', value: 'z' }, + { type: 'ImportDeclaration', value: 'a' }, + { type: 'ImportDeclaration', value: 't' }, + { type: 'ImportDeclaration', value: 'k' }, + { type: 'ImportDeclaration', value: 'BY' }, + { type: 'ImportDeclaration', value: 'Ba' }, + { type: 'ExpressionStatement', value: undefined }, + ]); +}); + +test('it returns default import module separations', () => { + const result = getImportNodes(code); + const sorted = getSortedNodes(result, { + importOrder: ['^a$', '^t$', '^k$', '^B'], + importOrderCaseInsensitive: false, + importOrderSeparation: true, + importOrderGroupNamespaceSpecifiers: false, + importOrderSortSpecifiers: false, + importOrderSideEffects: true, + importOrderSortByLength: null, + }); + expect(getSeparationData(sorted)).toEqual([ + { type: 'ImportDeclaration', value: 'XY' }, + { type: 'ImportDeclaration', value: 'Xa' }, + { type: 'ImportDeclaration', value: 'c' }, + { type: 'ImportDeclaration', value: 'g' }, + { type: 'ImportDeclaration', value: 'x' }, + { type: 'ImportDeclaration', value: 'z' }, + { type: 'ExpressionStatement', value: undefined }, + { type: 'ImportDeclaration', value: 'a' }, + { type: 'ExpressionStatement', value: undefined }, + { type: 'ImportDeclaration', value: 't' }, + { type: 'ExpressionStatement', value: undefined }, + { type: 'ImportDeclaration', value: 'k' }, + { type: 'ExpressionStatement', value: undefined }, + { type: 'ImportDeclaration', value: 'BY' }, + { type: 'ImportDeclaration', value: 'Ba' }, + { type: 'ExpressionStatement', value: undefined }, + { type: 'ExpressionStatement', value: undefined }, + ]); +}); + +test('it returns targeted import module separations', () => { + const result = getImportNodes(code); + const sorted = getSortedNodes(result, { + importOrder: ['^a$', '', '^t$', '', '^k$', '^B'], + importOrderCaseInsensitive: false, + importOrderSeparation: true, + importOrderGroupNamespaceSpecifiers: false, + importOrderSortSpecifiers: false, + importOrderSideEffects: true, + importOrderSortByLength: null, + }); + expect(getSeparationData(sorted)).toEqual([ + { type: 'ImportDeclaration', value: 'XY' }, + { type: 'ImportDeclaration', value: 'Xa' }, + { type: 'ImportDeclaration', value: 'c' }, + { type: 'ImportDeclaration', value: 'g' }, + { type: 'ImportDeclaration', value: 'x' }, + { type: 'ImportDeclaration', value: 'z' }, + { type: 'ImportDeclaration', value: 'a' }, + { type: 'ExpressionStatement', value: undefined }, + { type: 'ImportDeclaration', value: 't' }, + { type: 'ExpressionStatement', value: undefined }, + { type: 'ImportDeclaration', value: 'k' }, + { type: 'ImportDeclaration', value: 'BY' }, + { type: 'ImportDeclaration', value: 'Ba' }, + { type: 'ExpressionStatement', value: undefined }, + ]); +}); + +test('it never returns a separation at the top of the list (leading separator)', () => { + const result = getImportNodes( + ` + import './test'; + `.trim(), + ); + const sorted = getSortedNodes(result, { + importOrder: ['', '^[./]'], + importOrderCaseInsensitive: false, + importOrderSeparation: true, + importOrderGroupNamespaceSpecifiers: false, + importOrderSortSpecifiers: false, + importOrderSideEffects: true, + importOrderSortByLength: null, + }); + expect(getSeparationData(sorted)).toEqual([ + { type: 'ImportDeclaration', value: './test' }, + { type: 'ExpressionStatement', value: undefined }, + ]); +}); + +test('it never returns a separation at the top of the list (zero preceding imports)', () => { + const result = getImportNodes( + ` + import './test'; + `.trim(), + ); + const sorted = getSortedNodes(result, { + importOrder: ['^a.*$', '', '^[./]'], + importOrderCaseInsensitive: false, + importOrderSeparation: true, + importOrderGroupNamespaceSpecifiers: false, + importOrderSortSpecifiers: false, + importOrderSideEffects: true, + importOrderSortByLength: null, + }); + expect(getSeparationData(sorted)).toEqual([ + { type: 'ImportDeclaration', value: './test' }, + { type: 'ExpressionStatement', value: undefined }, + ]); +}); + +// Focuses the nodes solely to the import declarations and the new lines +function getSeparationData( + nodes: ImportOrLine[], +): { type: 'ImportDeclaration' | 'ExpressionStatement'; value?: string }[] { + return nodes + .filter( + (node) => + node.type === 'ImportDeclaration' || + (node.type === 'ExpressionStatement' && + node.expression.type === 'StringLiteral' && + node.expression.value === + PRETTIER_PLUGIN_SORT_IMPORTS_NEW_LINE), + ) + .map((x) => ({ + type: x.type, + value: x.type === 'ImportDeclaration' ? x.source.value : undefined, + })); +} diff --git a/src/utils/__tests__/get-sorted-nodes.spec.ts b/src/utils/__tests__/get-sorted-nodes.spec.ts index 17dca351..fb9ac6af 100644 --- a/src/utils/__tests__/get-sorted-nodes.spec.ts +++ b/src/utils/__tests__/get-sorted-nodes.spec.ts @@ -1,4 +1,5 @@ import { ImportDeclaration } from '@babel/types'; +import { expect, test } from 'vitest'; import { getImportNodes } from '../get-import-nodes'; import { getSortedNodes } from '../get-sorted-nodes'; @@ -46,6 +47,7 @@ test('it returns all sorted nodes', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; @@ -91,6 +93,7 @@ test('it returns all sorted nodes case-insensitive', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; @@ -136,6 +139,7 @@ test('it returns all sorted nodes with sort order', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; @@ -181,6 +185,7 @@ test('it returns all sorted nodes with sort order case-insensitive', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -225,6 +230,7 @@ test('it returns all sorted import nodes with sorted import specifiers', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: true, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -269,6 +275,7 @@ test('it returns all sorted import nodes with sorted import specifiers with case importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: true, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -313,6 +320,7 @@ test('it returns all sorted nodes with custom third party modules', () => { importOrderCaseInsensitive: true, importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ @@ -338,6 +346,7 @@ test('it returns all sorted nodes with namespace specifiers at the top', () => { importOrderSeparation: false, importOrderGroupNamespaceSpecifiers: true, importOrderSortSpecifiers: false, + importOrderSortByLength: null, importOrderSideEffects: true, }) as ImportDeclaration[]; @@ -356,6 +365,58 @@ test('it returns all sorted nodes with namespace specifiers at the top', () => { ]); }); +test('it returns all sorted nodes, sorted shortest to longest', () => { + const result = getImportNodes(code); + const sorted = getSortedNodes(result, { + importOrder: [], + importOrderCaseInsensitive: false, + importOrderSeparation: false, + importOrderGroupNamespaceSpecifiers: false, + importOrderSortSpecifiers: false, + importOrderSideEffects: true, + importOrderSortByLength: 'asc', + }) as ImportDeclaration[]; + expect(getSortedNodesNames(sorted)).toEqual([ + 'g', + 'z', + 'Ba', + 'BY', + 'Xa', + 'XY', + 'a', + 'x', + 'c', + 'k', + 't', + ]); +}); + +test('it returns all sorted nodes, sorted longest to shortest', () => { + const result = getImportNodes(code); + const sorted = getSortedNodes(result, { + importOrder: [], + importOrderCaseInsensitive: false, + importOrderSeparation: false, + importOrderGroupNamespaceSpecifiers: false, + importOrderSortSpecifiers: false, + importOrderSideEffects: false, + importOrderSortByLength: 'desc', + }) as ImportDeclaration[]; + expect(getSortedNodesNames(sorted)).toEqual([ + 't', + 'k', + 'c', + 'a', + 'x', + 'Ba', + 'BY', + 'Xa', + 'XY', + 'g', + 'z', + ]); +}); + test('it returns all sorted nodes with types', () => { const result = getImportNodes(typeCode, { plugins: ['typescript'], @@ -367,6 +428,7 @@ test('it returns all sorted nodes with types', () => { importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, importOrderSideEffects: true, + importOrderSortByLength: null, }) as ImportDeclaration[]; expect(getSortedNodesNames(sorted)).toEqual([ diff --git a/src/utils/__tests__/is-builtin-module.spec.ts b/src/utils/__tests__/is-builtin-module.spec.ts new file mode 100644 index 00000000..b84081fe --- /dev/null +++ b/src/utils/__tests__/is-builtin-module.spec.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest'; + +import { isBuiltinModule } from '../is-builtin-module.js'; + +describe('isBuiltinModule', () => { + it('should correctly identify traditional builtin modules', () => { + expect(isBuiltinModule('fs')).toBe(true); + expect(isBuiltinModule('path')).toBe(true); + expect(isBuiltinModule('http')).toBe(true); + expect(isBuiltinModule('crypto')).toBe(true); + expect(isBuiltinModule('util')).toBe(true); + expect(isBuiltinModule('os')).toBe(true); + expect(isBuiltinModule('events')).toBe(true); + }); + + it('should correctly identify node: prefixed builtin modules', () => { + expect(isBuiltinModule('node:fs')).toBe(true); + expect(isBuiltinModule('node:path')).toBe(true); + expect(isBuiltinModule('node:http')).toBe(true); + expect(isBuiltinModule('node:crypto')).toBe(true); + expect(isBuiltinModule('node:util')).toBe(true); + expect(isBuiltinModule('node:os')).toBe(true); + expect(isBuiltinModule('node:events')).toBe(true); + }); + + it('should return false for non-builtin modules', () => { + expect(isBuiltinModule('express')).toBe(false); + expect(isBuiltinModule('lodash')).toBe(false); + expect(isBuiltinModule('react')).toBe(false); + expect(isBuiltinModule('@types/node')).toBe(false); + expect(isBuiltinModule('./my-module')).toBe(false); + expect(isBuiltinModule('../other-module')).toBe(false); + }); + + it('should return false for invalid node: prefixes', () => { + expect(isBuiltinModule('node:express')).toBe(false); + expect(isBuiltinModule('node:lodash')).toBe(false); + expect(isBuiltinModule('node:nonexistent')).toBe(false); + }); + + it('should handle edge cases', () => { + expect(isBuiltinModule('')).toBe(false); + expect(isBuiltinModule('node:')).toBe(false); + expect(isBuiltinModule('node')).toBe(false); + }); +}); diff --git a/src/utils/__tests__/is-sort-imports-ignored.spec.ts b/src/utils/__tests__/is-sort-imports-ignored.spec.ts index d1429d9b..d6c00213 100644 --- a/src/utils/__tests__/is-sort-imports-ignored.spec.ts +++ b/src/utils/__tests__/is-sort-imports-ignored.spec.ts @@ -1,3 +1,6 @@ +import { expect, test } from 'vitest'; + +import { getAllCommentsFromNodes } from '../get-all-comments-from-nodes'; import { getImportNodes } from '../get-import-nodes'; import { isSortImportsIgnored } from '../is-sort-imports-ignored'; @@ -10,14 +13,30 @@ const codeNotIgnored = `// second comment import z from 'z'; `; +const notIgnoreTextWithIgnoreLine = `// you need to write sort-imports-ignore +import z from 'z'; +`; + test('it should return true if specific leading comment detected', () => { const importNodes = getImportNodes(codeIgnored); - expect(isSortImportsIgnored(importNodes)).toBeTruthy(); + expect( + isSortImportsIgnored(getAllCommentsFromNodes(importNodes)), + ).toBeTruthy(); }); test('it should return false if no specific leading comment detected', () => { const importNodes = getImportNodes(codeNotIgnored); - expect(isSortImportsIgnored(importNodes)).toBeFalsy(); + expect( + isSortImportsIgnored(getAllCommentsFromNodes(importNodes)), + ).toBeFalsy(); +}); + +test('it should return false if ignore isnt on top of comment', () => { + const importNodes = getImportNodes(notIgnoreTextWithIgnoreLine); + + expect( + isSortImportsIgnored(getAllCommentsFromNodes(importNodes)), + ).toBeFalsy(); }); diff --git a/src/utils/__tests__/remove-nodes-from-original-code.spec.ts b/src/utils/__tests__/remove-nodes-from-original-code.spec.ts deleted file mode 100644 index 21dd7858..00000000 --- a/src/utils/__tests__/remove-nodes-from-original-code.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { format } from 'prettier'; - -import { getAllCommentsFromNodes } from '../get-all-comments-from-nodes'; -import { getImportNodes } from '../get-import-nodes'; -import { getSortedNodes } from '../get-sorted-nodes'; -import { removeNodesFromOriginalCode } from '../remove-nodes-from-original-code'; - -const code = `// first comment -// second comment -import z from 'z'; -import c from 'c'; -import g from 'g'; -import t from 't'; -import k from 'k'; -// import a from 'a'; - // import a from 'a'; -import a from 'a'; -`; - -test('it should remove nodes from the original code', async () => { - const importNodes = getImportNodes(code); - const sortedNodes = getSortedNodes(importNodes, { - importOrder: [], - importOrderCaseInsensitive: false, - importOrderSeparation: false, - importOrderGroupNamespaceSpecifiers: false, - importOrderSortSpecifiers: false, - importOrderSideEffects: true, - }); - const allCommentsFromImports = getAllCommentsFromNodes(sortedNodes); - - const commentAndImportsToRemoveFromCode = [ - ...sortedNodes, - ...allCommentsFromImports, - ]; - const codeWithoutImportDeclarations = removeNodesFromOriginalCode( - code, - commentAndImportsToRemoveFromCode, - ); - const result = await format(codeWithoutImportDeclarations, { - parser: 'babel', - }); - expect(result).toEqual(''); -}); diff --git a/src/utils/__tests__/should-skip-file.spec.ts b/src/utils/__tests__/should-skip-file.spec.ts new file mode 100644 index 00000000..21e28a14 --- /dev/null +++ b/src/utils/__tests__/should-skip-file.spec.ts @@ -0,0 +1,69 @@ +import { describe, expect, it } from 'vitest'; + +import { shouldSkipFile } from '../should-skip-file'; + +describe('shouldSkipFile', () => { + it('should return false when skipPatterns is empty', () => { + expect(shouldSkipFile('src/file.ts', [])).toBe(false); + }); + + it('should return false when no patterns match', () => { + const patterns = ['test/*.ts', 'lib/*.js']; + expect(shouldSkipFile('src/file.ts', patterns)).toBe(false); + }); + + it('should return true when file matches a pattern', () => { + const patterns = ['src/*.ts', 'lib/*.js']; + expect(shouldSkipFile('src/file.ts', patterns)).toBe(true); + }); + + it('should handle glob patterns correctly', () => { + const patterns = ['*.test.ts', 'generated/**']; + expect(shouldSkipFile('Button.test.ts', patterns)).toBe(true); + expect(shouldSkipFile('generated/types.ts', patterns)).toBe(true); + expect(shouldSkipFile('src/Button.ts', patterns)).toBe(false); + }); + + it('should match filename-only patterns against basename', () => { + const patterns = ['*.js', 'example.ts']; + expect(shouldSkipFile('/long/path/to/file.js', patterns)).toBe(true); + expect(shouldSkipFile('/different/path/example.ts', patterns)).toBe( + true, + ); + expect(shouldSkipFile('/path/to/file.ts', patterns)).toBe(false); + }); + + it('should handle special characters in filenames', () => { + const patterns = ['*.spec.ts', '*test*.js']; + expect(shouldSkipFile('my-component.spec.ts', patterns)).toBe(true); + expect(shouldSkipFile('my.test.js', patterns)).toBe(true); + expect(shouldSkipFile('test.jsx', patterns)).toBe(false); + }); + + it('should handle multiple patterns with mixed path separators', () => { + const patterns = ['src/*.ts', 'test/*.js', '*.test.tsx']; + expect(shouldSkipFile('src/file.ts', patterns)).toBe(true); + expect(shouldSkipFile('test/file.js', patterns)).toBe(true); + expect(shouldSkipFile('component.test.tsx', patterns)).toBe(true); + expect(shouldSkipFile('src/sub/file.ts', patterns)).toBe(false); + }); + + it('should handle exact filename matches', () => { + const patterns = ['example.js', 'tsconfig.json']; + expect(shouldSkipFile('/any/path/example.js', patterns)).toBe(true); + expect(shouldSkipFile('/root/tsconfig.json', patterns)).toBe(true); + expect(shouldSkipFile('/path/to/example.test.js', patterns)).toBe( + false, + ); + }); + + it('should handle directory patterns', () => { + const patterns = ['test/**/*.*', 'generated/**/*.*']; + expect(shouldSkipFile('test/file.ts', patterns)).toBe(true); + expect(shouldSkipFile('test/unit/component.js', patterns)).toBe(true); + expect(shouldSkipFile('generated/types.ts', patterns)).toBe(true); + expect(shouldSkipFile('src/components/button.ts', patterns)).toBe( + false, + ); + }); +}); diff --git a/src/utils/adjust-comments-on-sorted-nodes.ts b/src/utils/adjust-comments-on-sorted-nodes.ts index 91396e9e..e4e13a67 100644 --- a/src/utils/adjust-comments-on-sorted-nodes.ts +++ b/src/utils/adjust-comments-on-sorted-nodes.ts @@ -1,5 +1,5 @@ import { ImportDeclaration, addComments, removeComments } from '@babel/types'; -import { clone, isEqual } from 'lodash'; +import { clone, isEqual } from 'lodash-es'; import { ImportOrLine } from '../types'; diff --git a/src/utils/assemble-updated-code.ts b/src/utils/assemble-updated-code.ts new file mode 100644 index 00000000..f02bc628 --- /dev/null +++ b/src/utils/assemble-updated-code.ts @@ -0,0 +1,66 @@ +import { Comment, Node } from '@babel/types'; + +type NodeOrComment = Node | Comment; +type BoundedNodeOrComment = NodeOrComment & { start: number; end: number }; + +interface InjectedCode { + type: 'InjectedCode'; + start: number; + end: number; +} + +/** + * Assembles the updated file, removing imports from the original file and + * injecting the sorted imports at the appropriate location. + * + * @param code the whole file as text + * @param nodes to be removed + * @param injectedCode the generated import source to be injected + * @param injectIdx the index at which to inject the generated source + */ +export const assembleUpdatedCode = ( + code: string, + nodes: (Node | Comment)[], + injectedCode?: string, + injectIdx: number = 0, +): string => { + const ranges: (BoundedNodeOrComment | InjectedCode)[] = nodes.filter( + (node): node is BoundedNodeOrComment => { + const start = Number(node.start); + const end = Number(node.end); + return Number.isSafeInteger(start) && Number.isSafeInteger(end); + }, + ); + if (injectedCode !== undefined) { + ranges.push({ + type: 'InjectedCode', + start: injectIdx, + end: injectIdx, + }); + } + ranges.sort((a, b) => a.start - b.start); + + let result: string = ''; + let idx = 0; + + for (const { type, start, end } of ranges) { + if (start > idx) { + result += code.slice(idx, start); + idx = start; + } + + if (injectedCode !== undefined && type === 'InjectedCode') { + result += injectedCode; + } + + if (end > idx) { + idx = end; + } + } + + if (idx < code.length) { + result += code.slice(idx); + } + + return result; +}; diff --git a/src/utils/create-ember-parsers.ts b/src/utils/create-ember-parsers.ts new file mode 100644 index 00000000..69111011 --- /dev/null +++ b/src/utils/create-ember-parsers.ts @@ -0,0 +1,10 @@ +export async function createEmberParsers() { + try { + // @ts-expect-error This plugin is not typed + const emberPlugin = await import('prettier-plugin-ember-template-tag'); + const { parsers } = (emberPlugin.default || emberPlugin) as any; + return { parsers }; + } catch { + return {}; + } +} diff --git a/src/utils/create-svelte-parsers.ts b/src/utils/create-svelte-parsers.ts index 1c72863d..25086ecc 100644 --- a/src/utils/create-svelte-parsers.ts +++ b/src/utils/create-svelte-parsers.ts @@ -1,8 +1,9 @@ -export function createSvelteParsers() { +export async function createSvelteParsers() { try { - var { parsers } = require('prettier-plugin-svelte'); + const sveltePlugin = await import('prettier-plugin-svelte'); + const { parsers } = (sveltePlugin.default || sveltePlugin) as any; + return { parsers }; } catch { return {}; } - return { parsers }; -} \ No newline at end of file +} diff --git a/src/utils/extract-ast-nodes.ts b/src/utils/extract-ast-nodes.ts index bc3c7f2f..2f8df4e5 100644 --- a/src/utils/extract-ast-nodes.ts +++ b/src/utils/extract-ast-nodes.ts @@ -1,28 +1,23 @@ import { ParseResult } from '@babel/parser'; -import traverse, { NodePath } from '@babel/traverse'; -import { - Directive, - File, - ImportDeclaration, -} from '@babel/types'; +import traverseModule, { NodePath } from '@babel/traverse'; +import { Directive, File, ImportDeclaration, Program } from '@babel/types'; + +const traverse = (traverseModule as any).default || traverseModule; export function extractASTNodes(ast: ParseResult) { const importNodes: ImportDeclaration[] = []; - const directives: Directive[] = []; + let injectIdx = 0; traverse(ast, { - Directive(path: NodePath) { - // Only capture directives if they are at the top scope of the source - // and their previous siblings are all directives - if ( - path.parent.type === 'Program' && - path.getAllPrevSiblings().every((s) => { - return s.type === 'Directive'; - }) - ) { - directives.push(path.node); - - // Trailing comments probably shouldn't be attached to the directive - path.node.trailingComments = null; + Program(path: NodePath) { + /** + * Imports will be injected before the first node of the body and + * its comments, skipping InterpreterDirective and Directive nodes. + * If the body is empty, default to 0, there will be no imports to + * inject anyway. + */ + for (const node of path.node.body) { + injectIdx = node.leadingComments?.[0]?.start ?? node.start ?? 0; + break; } }, @@ -35,5 +30,5 @@ export function extractASTNodes(ast: ParseResult) { } }, }); - return { importNodes, directives }; + return { importNodes, injectIdx }; } diff --git a/src/utils/get-code-from-ast.ts b/src/utils/get-code-from-ast.ts index 740afaf7..1fd7ce1b 100644 --- a/src/utils/get-code-from-ast.ts +++ b/src/utils/get-code-from-ast.ts @@ -1,10 +1,12 @@ -import generate from '@babel/generator'; -import { Directive, InterpreterDirective, Statement, file } from '@babel/types'; +import generateModule from '@babel/generator'; +import { Statement, file } from '@babel/types'; -import { newLineCharacters } from '../constants'; -import { getAllCommentsFromNodes } from './get-all-comments-from-nodes'; -import { removeNodesFromOriginalCode } from './remove-nodes-from-original-code'; +import { newLineCharacters } from '../constants.js'; import { PrettierOptions } from '../types'; +import { assembleUpdatedCode } from './assemble-updated-code.js'; +import { getAllCommentsFromNodes } from './get-all-comments-from-nodes.js'; + +const generate = (generateModule as any).default || generateModule; /** * This function generate a code string from the passed nodes. @@ -13,31 +15,19 @@ import { PrettierOptions } from '../types'; */ export const getCodeFromAst = ( nodes: Statement[], - directives: Directive[], originalCode: string, - interpreter?: InterpreterDirective | null, - options?: Pick + injectIdx: number = 0, + options?: Pick, ) => { const allCommentsFromImports = getAllCommentsFromNodes(nodes); - const nodesToRemoveFromCode = [ - ...directives, - ...nodes, - ...allCommentsFromImports, - ...(interpreter ? [interpreter] : []), - ]; - - const codeWithoutImportsAndInterpreter = removeNodesFromOriginalCode( - originalCode, - nodesToRemoveFromCode, - ); + const nodesToRemoveFromCode = [...nodes, ...allCommentsFromImports]; const newAST = file({ type: 'Program', body: nodes, - directives, + directives: [], sourceType: 'module', - interpreter: interpreter, leadingComments: [], innerComments: [], trailingComments: [], @@ -51,12 +41,17 @@ export const getCodeFromAst = ( }, }); - const { code } = generate(newAST, { importAttributesKeyword: options?.importOrderImportAttributesKeyword }); + const { code } = generate(newAST, { + importAttributesKeyword: options?.importOrderImportAttributesKeyword, + }); - return ( + return assembleUpdatedCode( + originalCode, + nodesToRemoveFromCode, code.replace( /"PRETTIER_PLUGIN_SORT_IMPORTS_NEW_LINE";/gi, newLineCharacters, - ) + codeWithoutImportsAndInterpreter.trim() + ), + injectIdx, ); }; diff --git a/src/utils/get-import-nodes-matched-group.ts b/src/utils/get-import-nodes-matched-group.ts index 2449b80b..ea1c64eb 100644 --- a/src/utils/get-import-nodes-matched-group.ts +++ b/src/utils/get-import-nodes-matched-group.ts @@ -1,10 +1,12 @@ import { ImportDeclaration } from '@babel/types'; import { + BUILTIN_MODULES_SPECIAL_WORD, THIRD_PARTY_MODULES_SPECIAL_WORD, THIRD_PARTY_TYPES_SPECIAL_WORD, TYPES_SPECIAL_WORD, -} from '../constants'; +} from '../constants.js'; +import { isBuiltinModule } from './is-builtin-module.js'; /** * Get the regexp group to keep the import nodes. @@ -15,6 +17,16 @@ export const getImportNodesMatchedGroup = ( node: ImportDeclaration, importOrder: string[], ) => { + const moduleName = node.source.value; + + // Check if this is a builtin module and is in the importOrder + if ( + importOrder.includes(BUILTIN_MODULES_SPECIAL_WORD) && + isBuiltinModule(moduleName) + ) { + return BUILTIN_MODULES_SPECIAL_WORD; + } + const groupWithRegExp = importOrder.map((group) => ({ group, regExp: group.startsWith(TYPES_SPECIAL_WORD) diff --git a/src/utils/get-sorted-import-specifiers.ts b/src/utils/get-sorted-import-specifiers.ts index 3897b787..a0397a92 100644 --- a/src/utils/get-sorted-import-specifiers.ts +++ b/src/utils/get-sorted-import-specifiers.ts @@ -1,6 +1,6 @@ import { ImportDeclaration } from '@babel/types'; -import { naturalSort } from '../natural-sort'; +import { naturalSort } from '../natural-sort/index.js'; /** * This function returns import nodes with alphabetically sorted module diff --git a/src/utils/get-sorted-nodes-by-import-order.ts b/src/utils/get-sorted-nodes-by-import-order.ts index d8ddd623..e0f663a5 100644 --- a/src/utils/get-sorted-nodes-by-import-order.ts +++ b/src/utils/get-sorted-nodes-by-import-order.ts @@ -1,11 +1,16 @@ -import { clone } from 'lodash'; +import { clone } from 'lodash-es'; -import { THIRD_PARTY_MODULES_SPECIAL_WORD, newLineNode } from '../constants'; -import { naturalSort } from '../natural-sort'; +import { + BUILTIN_MODULES_SPECIAL_WORD, + SEPARATOR_SPECIAL_WORD, + THIRD_PARTY_MODULES_SPECIAL_WORD, + newLineNode, +} from '../constants.js'; +import { naturalSort } from '../natural-sort/index.js'; import { GetSortedNodes, ImportGroups, ImportOrLine } from '../types'; -import { getImportNodesMatchedGroup } from './get-import-nodes-matched-group'; -import { getSortedImportSpecifiers } from './get-sorted-import-specifiers'; -import { getSortedNodesGroup } from './get-sorted-nodes-group'; +import { getImportNodesMatchedGroup } from './get-import-nodes-matched-group.js'; +import { getSortedImportSpecifiers } from './get-sorted-import-specifiers.js'; +import { getSortedNodesGroup } from './get-sorted-nodes-group.js'; /** * This function returns the given nodes, sorted in the order as indicated by @@ -17,7 +22,7 @@ import { getSortedNodesGroup } from './get-sorted-nodes-group'; export const getSortedNodesByImportOrder: GetSortedNodes = (nodes, options) => { naturalSort.insensitive = options.importOrderCaseInsensitive; - let { importOrder } = options; + let { importOrder, importOrderSortByLength } = options; const { importOrderSeparation, importOrderSortSpecifiers, @@ -39,25 +44,36 @@ export const getSortedNodesByImportOrder: GetSortedNodes = (nodes, options) => { {}, ); - const importOrderWithOutThirdPartyPlaceholder = importOrder.filter( - (group) => group !== THIRD_PARTY_MODULES_SPECIAL_WORD, + const importOrderWithOutSpecialWords = importOrder.filter( + (group) => + group !== THIRD_PARTY_MODULES_SPECIAL_WORD && + group !== BUILTIN_MODULES_SPECIAL_WORD, ); for (const node of originalNodes) { - const matchedGroup = getImportNodesMatchedGroup( - node, - importOrderWithOutThirdPartyPlaceholder, - ); + const matchedGroup = getImportNodesMatchedGroup(node, importOrder); importOrderGroups[matchedGroup].push(node); } + const hasUserProvidedSeparators = options.importOrder.includes( + SEPARATOR_SPECIAL_WORD, + ); + let safeToAddNewLine = false; for (const group of importOrder) { const groupNodes = importOrderGroups[group]; + if ( + importOrderSeparation && + group === SEPARATOR_SPECIAL_WORD && + safeToAddNewLine + ) { + finalNodes.push(newLineNode); + } if (groupNodes.length === 0) continue; const sortedInsideGroup = getSortedNodesGroup(groupNodes, { importOrderGroupNamespaceSpecifiers, + importOrderSortByLength, }); // Sort the import specifiers @@ -68,8 +84,9 @@ export const getSortedNodesByImportOrder: GetSortedNodes = (nodes, options) => { } finalNodes.push(...sortedInsideGroup); + safeToAddNewLine = true; - if (importOrderSeparation) { + if (importOrderSeparation && !hasUserProvidedSeparators) { finalNodes.push(newLineNode); } } diff --git a/src/utils/get-sorted-nodes-group.ts b/src/utils/get-sorted-nodes-group.ts index 89951336..759a0446 100644 --- a/src/utils/get-sorted-nodes-group.ts +++ b/src/utils/get-sorted-nodes-group.ts @@ -1,18 +1,36 @@ -import { Import, ImportDeclaration } from '@babel/types'; +import { ImportDeclaration } from '@babel/types'; -import { naturalSort } from '../natural-sort'; +import { naturalSort } from '../natural-sort/index.js'; import { PrettierOptions } from '../types'; export const getSortedNodesGroup = ( imports: ImportDeclaration[], - options: Pick, + options: Pick< + PrettierOptions, + 'importOrderGroupNamespaceSpecifiers' | 'importOrderSortByLength' + >, ) => { return imports.sort((a, b) => { + const aLength = (a.end || 0) - (a.start || 0); + const bLength = (b.end || 0) - (b.start || 0); + if (options.importOrderGroupNamespaceSpecifiers) { const diff = namespaceSpecifierSort(a, b); if (diff !== 0) return diff; } + if (options.importOrderSortByLength === 'asc') + return ( + aLength - bLength || + a.source.value.localeCompare(b.source.value) + ); + + if (options.importOrderSortByLength === 'desc') + return ( + bLength - aLength || + a.source.value.localeCompare(b.source.value) + ); + return naturalSort(a.source.value, b.source.value); }); }; diff --git a/src/utils/get-sorted-nodes.ts b/src/utils/get-sorted-nodes.ts index 4f5440b5..c22869fc 100644 --- a/src/utils/get-sorted-nodes.ts +++ b/src/utils/get-sorted-nodes.ts @@ -2,10 +2,10 @@ import { chunkSideEffectNode, chunkSideOtherNode, newLineNode, -} from '../constants'; +} from '../constants.js'; import { GetSortedNodes, ImportChunk, ImportOrLine } from '../types'; -import { adjustCommentsOnSortedNodes } from './adjust-comments-on-sorted-nodes'; -import { getSortedNodesByImportOrder } from './get-sorted-nodes-by-import-order'; +import { adjustCommentsOnSortedNodes } from './adjust-comments-on-sorted-nodes.js'; +import { getSortedNodesByImportOrder } from './get-sorted-nodes-by-import-order.js'; /** * This function returns the given nodes, sorted in the order as indicated by @@ -21,8 +21,7 @@ import { getSortedNodesByImportOrder } from './get-sorted-nodes-by-import-order' * @param options Options to influence the behavior of the sorting algorithm. */ export const getSortedNodes: GetSortedNodes = (nodes, options) => { - const { importOrderSeparation, importOrderSideEffects } = - options; + const { importOrderSeparation, importOrderSideEffects } = options; // Split nodes at each boundary between a side-effect node and a // non-side-effect node, keeping both types of nodes together. diff --git a/src/utils/is-builtin-module.ts b/src/utils/is-builtin-module.ts new file mode 100644 index 00000000..3e199e49 --- /dev/null +++ b/src/utils/is-builtin-module.ts @@ -0,0 +1,20 @@ +import { builtinModules } from 'module'; + +/** + * Check if a module name is a Node.js builtin module. + * This includes both the traditional names (e.g., 'fs') and the + * new node: prefixed names (e.g., 'node:fs'). + * + * @param moduleName The module name to check + * @returns True if the module is a Node.js builtin module + */ +export const isBuiltinModule = (moduleName: string): boolean => { + // Handle node: prefixed modules + if (moduleName.startsWith('node:')) { + const withoutPrefix = moduleName.slice(5); + return builtinModules.includes(withoutPrefix); + } + + // Handle traditional module names + return builtinModules.includes(moduleName); +}; diff --git a/src/utils/is-sort-imports-ignored.ts b/src/utils/is-sort-imports-ignored.ts index 6a1845fb..28e5fb58 100644 --- a/src/utils/is-sort-imports-ignored.ts +++ b/src/utils/is-sort-imports-ignored.ts @@ -1,12 +1,9 @@ -import { Statement } from '@babel/types'; +import { Comment } from '@babel/types'; -import { sortImportsIgnoredComment } from '../constants'; -import { getAllCommentsFromNodes } from './get-all-comments-from-nodes'; +import { sortImportsIgnoredComment } from '../constants.js'; -export const isSortImportsIgnored = (nodes: Statement[]) => - getAllCommentsFromNodes(nodes).some( - (comment) => - comment.loc && - comment.loc.start.line === 1 && - comment.value.includes(sortImportsIgnoredComment), +export const isSortImportsIgnored = (comments: Comment[]) => { + return comments.some((comment) => + comment.value.trimStart().startsWith(sortImportsIgnoredComment), ); +}; diff --git a/src/utils/remove-nodes-from-original-code.ts b/src/utils/remove-nodes-from-original-code.ts deleted file mode 100644 index a2c2a350..00000000 --- a/src/utils/remove-nodes-from-original-code.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { - CommentBlock, - CommentLine, - Directive, - ImportDeclaration, - InterpreterDirective, - Statement, -} from '@babel/types'; - -/** Escapes a string literal to be passed to new RegExp. See: https://stackoverflow.com/a/6969486/480608. - * @param s the string to escape - */ -const escapeRegExp = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - -/** - * Removes imports from original file - * @param code the whole file as text - * @param nodes to be removed - */ -export const removeNodesFromOriginalCode = ( - code: string, - nodes: ( - | Statement - | CommentBlock - | Directive - | CommentLine - | ImportDeclaration - | InterpreterDirective - )[], -) => { - let text = code; - for (const node of nodes) { - const start = Number(node.start); - const end = Number(node.end); - if (Number.isSafeInteger(start) && Number.isSafeInteger(end)) { - text = text.replace( - // only replace imports at the beginning of the line (ignoring whitespace) - // otherwise matching commented imports will be replaced - new RegExp( - '^\\s*' + escapeRegExp(code.substring(start, end)), - 'm', - ), - '', - ); - } - } - return text; -}; diff --git a/src/utils/replace-at.ts b/src/utils/replace-at.ts new file mode 100644 index 00000000..63c0e5cb --- /dev/null +++ b/src/utils/replace-at.ts @@ -0,0 +1,16 @@ +/** + * Replaces the contents of a string (str) at a given index with the replacement string - writes over as much text + * as the replacement string's length + * + * @param str + * @param index + * @param replacement + * @returns + */ +export function replaceAt(str: string, index: number, replacement: string) { + return ( + str.substring(0, index) + + replacement + + str.substring(index + replacement.length) + ); +} diff --git a/src/utils/should-skip-file.ts b/src/utils/should-skip-file.ts new file mode 100644 index 00000000..73c377d7 --- /dev/null +++ b/src/utils/should-skip-file.ts @@ -0,0 +1,33 @@ +import { minimatch } from 'minimatch'; +import path from 'path'; + +/** + * Checks if the current file path matches any of the patterns in importOrderExclude + * @param filePath The path of the current file being processed + * @param skipPatterns Array of patterns for files to skip + * @returns boolean indicating whether the file should be skipped + */ +export function shouldSkipFile( + filepath: string, + skipPatterns: string[], +): boolean { + if (skipPatterns.length === 0) { + return false; + } + + const normalizedPath = filepath.split(path.sep).join('/'); + const filename = path.basename(filepath); + + return skipPatterns.some((pattern) => { + // Normalize pattern to use forward slashes + const normalizedPattern = pattern.split(path.sep).join('/'); + + // If pattern doesn't contain '/', match against filename only + if (!normalizedPattern.includes('/')) { + return minimatch(filename, normalizedPattern, { matchBase: true }); + } + + // Otherwise match against full path + return minimatch(normalizedPath, normalizedPattern); + }); +} diff --git a/test-setup/raw-serializer.js b/test-setup/raw-serializer.mjs similarity index 93% rename from test-setup/raw-serializer.js rename to test-setup/raw-serializer.mjs index 20c66071..2e6fdefc 100644 --- a/test-setup/raw-serializer.js +++ b/test-setup/raw-serializer.mjs @@ -2,7 +2,7 @@ const RAW = Symbol.for('raw'); -module.exports = { +export default { print(val) { return val[RAW]; }, diff --git a/test-setup/run_spec.js b/test-setup/run_spec.mjs similarity index 81% rename from test-setup/run_spec.js rename to test-setup/run_spec.mjs index f9a3c191..30092f5b 100644 --- a/test-setup/run_spec.js +++ b/test-setup/run_spec.mjs @@ -1,13 +1,17 @@ 'use strict'; -const fs = require('fs'); -const extname = require('path').extname; -const prettier = require('prettier'); +import fs from 'fs'; +import { extname } from 'path'; +import prettier from 'prettier'; +import { expect } from 'vitest'; + +import plugin from '../src/'; +import rawSerializer from './raw-serializer.mjs'; function run_spec(dirname, parsers, options) { options = Object.assign( { - plugins: ['./src'], + plugins: [plugin], tabWidth: 4, }, options, @@ -24,7 +28,9 @@ function run_spec(dirname, parsers, options) { extname(filename) !== '.snap' && fs.lstatSync(path).isFile() && filename[0] !== '.' && - filename !== 'ppsi.spec.js' + filename !== 'ppsi.spec.js' && + filename !== 'ppsi.spec.mjs' && + filename !== 'ppsi.spec.cjs' ) { const source = read(path).replace(/\r\n/g, '\n'); @@ -34,11 +40,14 @@ function run_spec(dirname, parsers, options) { const output = prettyprint(source, path, mergedOptions); test(`${filename} - ${mergedOptions.parser}-verify`, async () => { try { + let result = await output; expect( - raw(source + '~'.repeat(80) + '\n' + (await output)), + raw(source + '~'.repeat(80) + '\n' + result), ).toMatchSnapshot(filename); } catch (e) { - console.error(e, path); + throw new Error( + `Problem occurred in ${path} file: ${e.name}`, + ); } }); @@ -58,6 +67,10 @@ function run_spec(dirname, parsers, options) { } }); } + +// Add custom snapshot serializer for Vitest +expect.addSnapshotSerializer(rawSerializer); + global.run_spec = run_spec; function stripLocation(ast) { diff --git a/tests/Angular/__snapshots__/ppsi.spec.js.snap b/tests/Angular/__snapshots__/ppsi.spec.mjs.snap similarity index 94% rename from tests/Angular/__snapshots__/ppsi.spec.js.snap rename to tests/Angular/__snapshots__/ppsi.spec.mjs.snap index c9357cd7..6c1b0ed3 100644 --- a/tests/Angular/__snapshots__/ppsi.spec.js.snap +++ b/tests/Angular/__snapshots__/ppsi.spec.mjs.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`imports-with-decorators.js - typescript-verify: imports-with-decorators.js 1`] = ` +exports[`imports-with-decorators.js - typescript-verify > imports-with-decorators.js 1`] = ` import { Body, Controller, HttpService, Logger, Post } from "@core/common"; import { retry } from "@server/operators"; diff --git a/tests/Angular/ppsi.spec.js b/tests/Angular/ppsi.spec.mjs similarity index 54% rename from tests/Angular/ppsi.spec.js rename to tests/Angular/ppsi.spec.mjs index d1bea31e..b23ade70 100644 --- a/tests/Angular/ppsi.spec.js +++ b/tests/Angular/ppsi.spec.mjs @@ -1,5 +1,5 @@ run_spec(__dirname, ["typescript"], { importOrder: ['^@core/(.*)$', '^@server/(.*)', '^@ui/(.*)$', '^[./]'], importOrderSeparation: true, - importOrderParserPlugins : ["typescript", "classProperties", "[\"decorators\", { \"decoratorsBeforeExport\": true }]"] + importOrderParserPlugins: ["typescript", "classProperties", "[\"decorators\", { \"decoratorsBeforeExport\": true }]"] }); diff --git a/tests/Babel/__snapshots__/ppsi.spec.js.snap b/tests/Babel/__snapshots__/ppsi.spec.mjs.snap similarity index 90% rename from tests/Babel/__snapshots__/ppsi.spec.js.snap rename to tests/Babel/__snapshots__/ppsi.spec.mjs.snap index 84c62858..b3c3cafe 100644 --- a/tests/Babel/__snapshots__/ppsi.spec.js.snap +++ b/tests/Babel/__snapshots__/ppsi.spec.mjs.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`imports-with-comments.js - babel-verify: imports-with-comments.js 1`] = ` +exports[`imports-with-comments.js - babel-verify > imports-with-comments.js 1`] = ` // I am top level comment in this file. import z from 'z'; import threeLevelRelativePath from "../../../threeLevelRelativePath"; diff --git a/tests/Babel/ppsi.spec.js b/tests/Babel/ppsi.spec.mjs similarity index 100% rename from tests/Babel/ppsi.spec.js rename to tests/Babel/ppsi.spec.mjs diff --git a/tests/BuiltinModules/__snapshots__/ppsi.spec.mjs.snap b/tests/BuiltinModules/__snapshots__/ppsi.spec.mjs.snap new file mode 100644 index 00000000..66a61eb8 --- /dev/null +++ b/tests/BuiltinModules/__snapshots__/ppsi.spec.mjs.snap @@ -0,0 +1,28 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`builtin-modules.ts - typescript-verify > builtin-modules.ts 1`] = ` +import express from 'express'; +import lodash from 'lodash'; +import fs from 'fs'; +import path from 'path'; +import http from 'http'; +import { readFile } from 'node:fs'; +import { join } from 'node:path'; +import crypto from 'node:crypto'; +import util from 'util'; + +import { myFunction } from './my-module'; +import { anotherFunction } from '../other-module';~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import fs from "fs"; +import http from "http"; +import crypto from "node:crypto"; +import { readFile } from "node:fs"; +import { join } from "node:path"; +import path from "path"; +import util from "util"; +import express from "express"; +import lodash from "lodash"; +import { anotherFunction } from "../other-module"; +import { myFunction } from "./my-module"; + +`; diff --git a/tests/BuiltinModules/builtin-modules.ts b/tests/BuiltinModules/builtin-modules.ts new file mode 100644 index 00000000..49d09deb --- /dev/null +++ b/tests/BuiltinModules/builtin-modules.ts @@ -0,0 +1,12 @@ +import express from 'express'; +import lodash from 'lodash'; +import fs from 'fs'; +import path from 'path'; +import http from 'http'; +import { readFile } from 'node:fs'; +import { join } from 'node:path'; +import crypto from 'node:crypto'; +import util from 'util'; + +import { myFunction } from './my-module'; +import { anotherFunction } from '../other-module'; \ No newline at end of file diff --git a/tests/BuiltinModules/ppsi.spec.mjs b/tests/BuiltinModules/ppsi.spec.mjs new file mode 100644 index 00000000..9c7f7691 --- /dev/null +++ b/tests/BuiltinModules/ppsi.spec.mjs @@ -0,0 +1,4 @@ +run_spec(__dirname, ['typescript'], { + importOrder: ['', '', '^[./]'], + importOrderParserPlugins: ['typescript'] +}); \ No newline at end of file diff --git a/tests/BuiltinModulesDisabled/__snapshots__/ppsi.spec.mjs.snap b/tests/BuiltinModulesDisabled/__snapshots__/ppsi.spec.mjs.snap new file mode 100644 index 00000000..c7c59a4f --- /dev/null +++ b/tests/BuiltinModulesDisabled/__snapshots__/ppsi.spec.mjs.snap @@ -0,0 +1,28 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`builtin-modules.ts - typescript-verify > builtin-modules.ts 1`] = ` +import express from 'express'; +import lodash from 'lodash'; +import fs from 'fs'; +import path from 'path'; +import http from 'http'; +import { readFile } from 'node:fs'; +import { join } from 'node:path'; +import crypto from 'node:crypto'; +import util from 'util'; + +import { myFunction } from './my-module'; +import { anotherFunction } from '../other-module';~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import express from "express"; +import fs from "fs"; +import http from "http"; +import lodash from "lodash"; +import crypto from "node:crypto"; +import { readFile } from "node:fs"; +import { join } from "node:path"; +import path from "path"; +import util from "util"; +import { anotherFunction } from "../other-module"; +import { myFunction } from "./my-module"; + +`; diff --git a/tests/BuiltinModulesDisabled/builtin-modules.ts b/tests/BuiltinModulesDisabled/builtin-modules.ts new file mode 100644 index 00000000..49d09deb --- /dev/null +++ b/tests/BuiltinModulesDisabled/builtin-modules.ts @@ -0,0 +1,12 @@ +import express from 'express'; +import lodash from 'lodash'; +import fs from 'fs'; +import path from 'path'; +import http from 'http'; +import { readFile } from 'node:fs'; +import { join } from 'node:path'; +import crypto from 'node:crypto'; +import util from 'util'; + +import { myFunction } from './my-module'; +import { anotherFunction } from '../other-module'; \ No newline at end of file diff --git a/tests/BuiltinModulesDisabled/ppsi.spec.mjs b/tests/BuiltinModulesDisabled/ppsi.spec.mjs new file mode 100644 index 00000000..b5bce501 --- /dev/null +++ b/tests/BuiltinModulesDisabled/ppsi.spec.mjs @@ -0,0 +1,4 @@ +run_spec(__dirname, ['typescript'], { + importOrder: ['^[./]'], + importOrderParserPlugins: ['typescript'] +}); \ No newline at end of file diff --git a/tests/BuiltinModulesWithCustomOrder/__snapshots__/ppsi.spec.mjs.snap b/tests/BuiltinModulesWithCustomOrder/__snapshots__/ppsi.spec.mjs.snap new file mode 100644 index 00000000..66a61eb8 --- /dev/null +++ b/tests/BuiltinModulesWithCustomOrder/__snapshots__/ppsi.spec.mjs.snap @@ -0,0 +1,28 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`builtin-modules.ts - typescript-verify > builtin-modules.ts 1`] = ` +import express from 'express'; +import lodash from 'lodash'; +import fs from 'fs'; +import path from 'path'; +import http from 'http'; +import { readFile } from 'node:fs'; +import { join } from 'node:path'; +import crypto from 'node:crypto'; +import util from 'util'; + +import { myFunction } from './my-module'; +import { anotherFunction } from '../other-module';~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import fs from "fs"; +import http from "http"; +import crypto from "node:crypto"; +import { readFile } from "node:fs"; +import { join } from "node:path"; +import path from "path"; +import util from "util"; +import express from "express"; +import lodash from "lodash"; +import { anotherFunction } from "../other-module"; +import { myFunction } from "./my-module"; + +`; diff --git a/tests/BuiltinModulesWithCustomOrder/builtin-modules.ts b/tests/BuiltinModulesWithCustomOrder/builtin-modules.ts new file mode 100644 index 00000000..49d09deb --- /dev/null +++ b/tests/BuiltinModulesWithCustomOrder/builtin-modules.ts @@ -0,0 +1,12 @@ +import express from 'express'; +import lodash from 'lodash'; +import fs from 'fs'; +import path from 'path'; +import http from 'http'; +import { readFile } from 'node:fs'; +import { join } from 'node:path'; +import crypto from 'node:crypto'; +import util from 'util'; + +import { myFunction } from './my-module'; +import { anotherFunction } from '../other-module'; \ No newline at end of file diff --git a/tests/BuiltinModulesWithCustomOrder/ppsi.spec.mjs b/tests/BuiltinModulesWithCustomOrder/ppsi.spec.mjs new file mode 100644 index 00000000..9c7f7691 --- /dev/null +++ b/tests/BuiltinModulesWithCustomOrder/ppsi.spec.mjs @@ -0,0 +1,4 @@ +run_spec(__dirname, ['typescript'], { + importOrder: ['', '', '^[./]'], + importOrderParserPlugins: ['typescript'] +}); \ No newline at end of file diff --git a/tests/Ember/__snapshots__/ppsi.spec.mjs.snap b/tests/Ember/__snapshots__/ppsi.spec.mjs.snap new file mode 100644 index 00000000..6ae25847 --- /dev/null +++ b/tests/Ember/__snapshots__/ppsi.spec.mjs.snap @@ -0,0 +1,141 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`sfc.gjs - ember-template-tag-verify > sfc.gjs 1`] = ` +// I am top level comment in this file. +import z from 'z'; +import threeLevelRelativePath from '../../../threeLevelRelativePath'; +import sameLevelRelativePath from './sameLevelRelativePath'; +import thirdParty from 'third-party'; +import oneLevelRelativePath from '../oneLevelRelativePath'; +import otherthing from '@core/otherthing'; +import abc from '@core/abc'; +import twoLevelRelativePath from '../../twoLevelRelativePath'; +import component from '@ui/hello'; +import fourLevelRelativePath from '../../../../fourLevelRelativePath'; +import something from '@server/something'; +import xyz from '@ui/xyz'; +import Component from '@glimmer/component'; +import { action } from '@ember/object'; + +const what = ; + +export const who = ; + +export default class Foo extends Component { + @action + myCoolFunction(){} + + +}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import { action } from "@ember/object"; +import Component from "@glimmer/component"; +import thirdParty from "third-party"; +import z from "z"; + +import abc from "@core/abc"; +import otherthing from "@core/otherthing"; + +import something from "@server/something"; + +import component from "@ui/hello"; +import xyz from "@ui/xyz"; + +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; + +// I am top level comment in this file. + +const what = ; + +export const who = ; + +export default class Foo extends Component { + @action + myCoolFunction() {} + + +} + +`; + +exports[`sfc.gts - ember-template-tag-verify > sfc.gts 1`] = ` +// I am top level comment in this file. +import z from 'z'; +import threeLevelRelativePath from '../../../threeLevelRelativePath'; +import sameLevelRelativePath from './sameLevelRelativePath'; +import thirdParty from 'third-party'; +import oneLevelRelativePath from '../oneLevelRelativePath'; +import otherthing from '@core/otherthing'; +import abc from '@core/abc'; +import twoLevelRelativePath from '../../twoLevelRelativePath'; +import component from '@ui/hello'; +import fourLevelRelativePath from '../../../../fourLevelRelativePath'; +import * as a from 'a'; +import type RouterService from '@ember/routing/router-service'; +import something from '@server/something'; +import xyz from '@ui/xyz'; +import Component from '@glimmer/component'; +import { action } from '@ember/object'; + +interface FooSignature { + Args: { + bar: string + }; +} + +const what = ; + +export const who = ; + +export default class Foo extends Component { + @action + myCoolFunction(){} + + +}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import { action } from "@ember/object"; +import type RouterService from "@ember/routing/router-service"; +import Component from "@glimmer/component"; +import * as a from "a"; +import thirdParty from "third-party"; +import z from "z"; + +import abc from "@core/abc"; +import otherthing from "@core/otherthing"; + +import something from "@server/something"; + +import component from "@ui/hello"; +import xyz from "@ui/xyz"; + +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; + +// I am top level comment in this file. + +interface FooSignature { + Args: { bar: string }; +} + +const what = ; + +export const who = ; + +export default class Foo extends Component { + @action + myCoolFunction() {} + + +} + +`; diff --git a/tests/Ember/ppsi.spec.mjs b/tests/Ember/ppsi.spec.mjs new file mode 100644 index 00000000..b176d80c --- /dev/null +++ b/tests/Ember/ppsi.spec.mjs @@ -0,0 +1,7 @@ +import plugin from '../../src'; + +run_spec(__dirname, ['ember-template-tag'], { + importOrder: ['^@core/(.*)$', '^@server/(.*)', '^@ui/(.*)$', '^[./]'], + importOrderSeparation: true, + plugins: ['prettier-plugin-ember-template-tag', plugin] +}); diff --git a/tests/Ember/sfc.gjs b/tests/Ember/sfc.gjs new file mode 100644 index 00000000..05492e88 --- /dev/null +++ b/tests/Ember/sfc.gjs @@ -0,0 +1,28 @@ +// I am top level comment in this file. +import z from 'z'; +import threeLevelRelativePath from '../../../threeLevelRelativePath'; +import sameLevelRelativePath from './sameLevelRelativePath'; +import thirdParty from 'third-party'; +import oneLevelRelativePath from '../oneLevelRelativePath'; +import otherthing from '@core/otherthing'; +import abc from '@core/abc'; +import twoLevelRelativePath from '../../twoLevelRelativePath'; +import component from '@ui/hello'; +import fourLevelRelativePath from '../../../../fourLevelRelativePath'; +import something from '@server/something'; +import xyz from '@ui/xyz'; +import Component from '@glimmer/component'; +import { action } from '@ember/object'; + +const what = ; + +export const who = ; + +export default class Foo extends Component { + @action + myCoolFunction(){} + + +} \ No newline at end of file diff --git a/tests/Ember/sfc.gts b/tests/Ember/sfc.gts new file mode 100644 index 00000000..c4eb1a6b --- /dev/null +++ b/tests/Ember/sfc.gts @@ -0,0 +1,36 @@ +// I am top level comment in this file. +import z from 'z'; +import threeLevelRelativePath from '../../../threeLevelRelativePath'; +import sameLevelRelativePath from './sameLevelRelativePath'; +import thirdParty from 'third-party'; +import oneLevelRelativePath from '../oneLevelRelativePath'; +import otherthing from '@core/otherthing'; +import abc from '@core/abc'; +import twoLevelRelativePath from '../../twoLevelRelativePath'; +import component from '@ui/hello'; +import fourLevelRelativePath from '../../../../fourLevelRelativePath'; +import * as a from 'a'; +import type RouterService from '@ember/routing/router-service'; +import something from '@server/something'; +import xyz from '@ui/xyz'; +import Component from '@glimmer/component'; +import { action } from '@ember/object'; + +interface FooSignature { + Args: { + bar: string + }; +} + +const what = ; + +export const who = ; + +export default class Foo extends Component { + @action + myCoolFunction(){} + + +} \ No newline at end of file diff --git a/tests/Flow/__snapshots__/ppsi.spec.js.snap b/tests/Flow/__snapshots__/ppsi.spec.mjs.snap similarity index 92% rename from tests/Flow/__snapshots__/ppsi.spec.js.snap rename to tests/Flow/__snapshots__/ppsi.spec.mjs.snap index 0703b7dc..2ed522c6 100644 --- a/tests/Flow/__snapshots__/ppsi.spec.js.snap +++ b/tests/Flow/__snapshots__/ppsi.spec.mjs.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`imports-with-comments.js - flow-verify: imports-with-comments.js 1`] = ` +exports[`imports-with-comments.js - flow-verify > imports-with-comments.js 1`] = ` /** * @flow */ diff --git a/tests/Flow/ppsi.spec.js b/tests/Flow/ppsi.spec.mjs similarity index 100% rename from tests/Flow/ppsi.spec.js rename to tests/Flow/ppsi.spec.mjs diff --git a/tests/ImportPreventSortingSideEffects/__snapshots__/ppsi.spec.js.snap b/tests/ImportPreventSortingSideEffects/__snapshots__/ppsi.spec.mjs.snap similarity index 97% rename from tests/ImportPreventSortingSideEffects/__snapshots__/ppsi.spec.js.snap rename to tests/ImportPreventSortingSideEffects/__snapshots__/ppsi.spec.mjs.snap index fba106ea..fe63d9de 100644 --- a/tests/ImportPreventSortingSideEffects/__snapshots__/ppsi.spec.js.snap +++ b/tests/ImportPreventSortingSideEffects/__snapshots__/ppsi.spec.mjs.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`imports-with-side-effect-imports.js - typescript-verify: imports-with-side-effect-imports.js 1`] = ` +exports[`imports-with-side-effect-imports.js - typescript-verify > imports-with-side-effect-imports.js 1`] = ` // I am top level comment in this file. import thirdParty0 from "third-party0"; import something3 from "@core/something3"; diff --git a/tests/ImportPreventSortingSideEffects/ppsi.spec.js b/tests/ImportPreventSortingSideEffects/ppsi.spec.mjs similarity index 100% rename from tests/ImportPreventSortingSideEffects/ppsi.spec.js rename to tests/ImportPreventSortingSideEffects/ppsi.spec.mjs diff --git a/tests/ImportsNotSeparated/__snapshots__/ppsi.spec.js.snap b/tests/ImportsNotSeparated/__snapshots__/ppsi.spec.mjs.snap similarity index 91% rename from tests/ImportsNotSeparated/__snapshots__/ppsi.spec.js.snap rename to tests/ImportsNotSeparated/__snapshots__/ppsi.spec.mjs.snap index 79565021..f5ada62a 100644 --- a/tests/ImportsNotSeparated/__snapshots__/ppsi.spec.js.snap +++ b/tests/ImportsNotSeparated/__snapshots__/ppsi.spec.mjs.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`import-export-in-between.ts - typescript-verify: import-export-in-between.ts 1`] = ` +exports[`import-export-in-between.ts - typescript-verify > import-export-in-between.ts 1`] = ` import threeLevelRelativePath from "../../../threeLevelRelativePath"; import sameLevelRelativePath from "./sameLevelRelativePath"; import thirdParty from "third-party"; @@ -47,7 +47,7 @@ function add(a: number, b: number) { `; -exports[`import-export-only.ts - typescript-verify: import-export-only.ts 1`] = ` +exports[`import-export-only.ts - typescript-verify > import-export-only.ts 1`] = ` import React from 'react'; export const a = 1; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -57,7 +57,7 @@ export const a = 1; `; -exports[`imports-with-comments.ts - typescript-verify: imports-with-comments.ts 1`] = ` +exports[`imports-with-comments.ts - typescript-verify > imports-with-comments.ts 1`] = ` // I am top level comment in this file. // I am second line of top level comment in this file. import './commands'; @@ -82,7 +82,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-comments-and-third-party.ts - typescript-verify: imports-with-comments-and-third-party.ts 1`] = ` +exports[`imports-with-comments-and-third-party.ts - typescript-verify > imports-with-comments-and-third-party.ts 1`] = ` // I am top level comment in this file. // I am second line of top level comment in this file. import './commands'; @@ -108,7 +108,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-comments-on-top.ts - typescript-verify: imports-with-comments-on-top.ts 1`] = ` +exports[`imports-with-comments-on-top.ts - typescript-verify > imports-with-comments-on-top.ts 1`] = ` // I am top level comment in this file. // I am second line of top level comment in this file. import z from 'z'; @@ -151,7 +151,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-directives.ts - typescript-verify: imports-with-directives.ts 1`] = ` +exports[`imports-with-directives.ts - typescript-verify > imports-with-directives.ts 1`] = ` 'use strict'; 'use client'; import otherthing from "@core/otherthing"; @@ -180,7 +180,6 @@ const workletAdd = (a:number,b:number) => { ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "use strict"; "use client"; - import abc from "@core/abc"; import otherthing from "@core/otherthing"; @@ -208,7 +207,7 @@ const workletAdd = (a: number, b: number) => { `; -exports[`imports-with-file-level-comments.ts - typescript-verify: imports-with-file-level-comments.ts 1`] = ` +exports[`imports-with-file-level-comments.ts - typescript-verify > imports-with-file-level-comments.ts 1`] = ` //@ts-ignore // I am file top level comments import threeLevelRelativePath from "../../../threeLevelRelativePath"; @@ -281,7 +280,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-interpreter-directive.ts - typescript-verify: imports-with-interpreter-directive.ts 1`] = ` +exports[`imports-with-interpreter-directive.ts - typescript-verify > imports-with-interpreter-directive.ts 1`] = ` #!/usr/bin/env node import otherthing from "@core/otherthing"; import abc from "@core/abc"; @@ -312,7 +311,7 @@ function add(a: number, b: number) { `; -exports[`imports-without-third-party.ts - typescript-verify: imports-without-third-party.ts 1`] = ` +exports[`imports-without-third-party.ts - typescript-verify > imports-without-third-party.ts 1`] = ` // I am top level comment import otherthing from "@core/otherthing"; import abc from "@core/abc"; @@ -333,7 +332,7 @@ import twoLevelRelativePath from "../../twoLevelRelativePath"; `; -exports[`no-import-export.ts - typescript-verify: no-import-export.ts 1`] = ` +exports[`no-import-export.ts - typescript-verify > no-import-export.ts 1`] = ` function add(a:number,b:number) { return a + b; } @@ -344,7 +343,7 @@ function add(a: number, b: number) { `; -exports[`one-import.ts - typescript-verify: one-import.ts 1`] = ` +exports[`one-import.ts - typescript-verify > one-import.ts 1`] = ` // This example support/index.js is processed and // loaded automatically before your test files. // @@ -386,7 +385,7 @@ import "./commands"; `; -exports[`sort-imports-ignored.ts - typescript-verify: sort-imports-ignored.ts 1`] = ` +exports[`sort-imports-ignored.ts - typescript-verify > sort-imports-ignored.ts 1`] = ` // sort-imports-ignore import './commands'; diff --git a/tests/ImportsNotSeparated/ppsi.spec.js b/tests/ImportsNotSeparated/ppsi.spec.mjs similarity index 100% rename from tests/ImportsNotSeparated/ppsi.spec.js rename to tests/ImportsNotSeparated/ppsi.spec.mjs diff --git a/tests/ImportsNotSeparatedRest/__snapshots__/ppsi.spec.js.snap b/tests/ImportsNotSeparatedRest/__snapshots__/ppsi.spec.mjs.snap similarity index 91% rename from tests/ImportsNotSeparatedRest/__snapshots__/ppsi.spec.js.snap rename to tests/ImportsNotSeparatedRest/__snapshots__/ppsi.spec.mjs.snap index ecc8d513..5e70bc36 100644 --- a/tests/ImportsNotSeparatedRest/__snapshots__/ppsi.spec.js.snap +++ b/tests/ImportsNotSeparatedRest/__snapshots__/ppsi.spec.mjs.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`import-export-in-between.ts - typescript-verify: import-export-in-between.ts 1`] = ` +exports[`import-export-in-between.ts - typescript-verify > import-export-in-between.ts 1`] = ` import threeLevelRelativePath from "../../../threeLevelRelativePath"; import sameLevelRelativePath from "./sameLevelRelativePath"; import thirdParty from "third-party"; @@ -47,7 +47,7 @@ function add(a: number, b: number) { `; -exports[`import-export-only.ts - typescript-verify: import-export-only.ts 1`] = ` +exports[`import-export-only.ts - typescript-verify > import-export-only.ts 1`] = ` import React from 'react'; export const a = 1; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -57,7 +57,7 @@ export const a = 1; `; -exports[`imports-with-comments.ts - typescript-verify: imports-with-comments.ts 1`] = ` +exports[`imports-with-comments.ts - typescript-verify > imports-with-comments.ts 1`] = ` // I am top level comment in this file. // I am second line of top level comment in this file. import './commands'; @@ -82,7 +82,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-comments-and-third-party.ts - typescript-verify: imports-with-comments-and-third-party.ts 1`] = ` +exports[`imports-with-comments-and-third-party.ts - typescript-verify > imports-with-comments-and-third-party.ts 1`] = ` // I am top level comment in this file. // I am second line of top level comment in this file. import './commands'; @@ -108,7 +108,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-comments-on-top.ts - typescript-verify: imports-with-comments-on-top.ts 1`] = ` +exports[`imports-with-comments-on-top.ts - typescript-verify > imports-with-comments-on-top.ts 1`] = ` // I am top level comment in this file. // I am second line of top level comment in this file. import z from 'z'; @@ -151,7 +151,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-file-level-comments.ts - typescript-verify: imports-with-file-level-comments.ts 1`] = ` +exports[`imports-with-file-level-comments.ts - typescript-verify > imports-with-file-level-comments.ts 1`] = ` //@ts-ignore // I am file top level comments import threeLevelRelativePath from "../../../threeLevelRelativePath"; @@ -224,7 +224,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-interpreter-directive.ts - typescript-verify: imports-with-interpreter-directive.ts 1`] = ` +exports[`imports-with-interpreter-directive.ts - typescript-verify > imports-with-interpreter-directive.ts 1`] = ` #!/usr/bin/env node import otherthing from "@core/otherthing"; import abc from "@core/abc"; @@ -255,7 +255,7 @@ function add(a: number, b: number) { `; -exports[`imports-without-third-party.ts - typescript-verify: imports-without-third-party.ts 1`] = ` +exports[`imports-without-third-party.ts - typescript-verify > imports-without-third-party.ts 1`] = ` // I am top level comment import otherthing from "@core/otherthing"; import abc from "@core/abc"; @@ -276,7 +276,7 @@ import twoLevelRelativePath from "../../twoLevelRelativePath"; `; -exports[`no-import-export.ts - typescript-verify: no-import-export.ts 1`] = ` +exports[`no-import-export.ts - typescript-verify > no-import-export.ts 1`] = ` function add(a:number,b:number) { return a + b; } @@ -287,7 +287,7 @@ function add(a: number, b: number) { `; -exports[`one-import.ts - typescript-verify: one-import.ts 1`] = ` +exports[`one-import.ts - typescript-verify > one-import.ts 1`] = ` // This example support/index.js is processed and // loaded automatically before your test files. // @@ -329,7 +329,7 @@ import "./commands"; `; -exports[`sort-imports-ignored.ts - typescript-verify: sort-imports-ignored.ts 1`] = ` +exports[`sort-imports-ignored.ts - typescript-verify > sort-imports-ignored.ts 1`] = ` // sort-imports-ignore import './commands'; diff --git a/tests/ImportsNotSeparatedRest/ppsi.spec.js b/tests/ImportsNotSeparatedRest/ppsi.spec.mjs similarity index 100% rename from tests/ImportsNotSeparatedRest/ppsi.spec.js rename to tests/ImportsNotSeparatedRest/ppsi.spec.mjs diff --git a/tests/ImportsSeparated/__snapshots__/ppsi.spec.js.snap b/tests/ImportsSeparated/__snapshots__/ppsi.spec.mjs.snap similarity index 91% rename from tests/ImportsSeparated/__snapshots__/ppsi.spec.js.snap rename to tests/ImportsSeparated/__snapshots__/ppsi.spec.mjs.snap index bb5d393f..aa22efad 100644 --- a/tests/ImportsSeparated/__snapshots__/ppsi.spec.js.snap +++ b/tests/ImportsSeparated/__snapshots__/ppsi.spec.mjs.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`import-export-in-between.ts - typescript-verify: import-export-in-between.ts 1`] = ` +exports[`import-export-in-between.ts - typescript-verify > import-export-in-between.ts 1`] = ` import threeLevelRelativePath from "../../../threeLevelRelativePath"; import sameLevelRelativePath from "./sameLevelRelativePath"; import thirdParty from "third-party"; @@ -51,7 +51,7 @@ function add(a: number, b: number) { `; -exports[`import-export-only.ts - typescript-verify: import-export-only.ts 1`] = ` +exports[`import-export-only.ts - typescript-verify > import-export-only.ts 1`] = ` import React from 'react'; export const a = 1; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -61,7 +61,7 @@ export const a = 1; `; -exports[`import-with-type-imports-together.ts - typescript-verify: import-with-type-imports-together.ts 1`] = ` +exports[`import-with-type-imports-together.ts - typescript-verify > import-with-type-imports-together.ts 1`] = ` import { foo } from "@server/foo" import type { Quux } from "./quux" import { Link } from "@ui/Link" @@ -80,7 +80,7 @@ import type { Quux } from "./quux"; `; -exports[`imports-with-comments.ts - typescript-verify: imports-with-comments.ts 1`] = ` +exports[`imports-with-comments.ts - typescript-verify > imports-with-comments.ts 1`] = ` // I am top level comment in this file. // I am second line of top level comment in this file. import './commands'; @@ -105,7 +105,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-comments-and-third-party.ts - typescript-verify: imports-with-comments-and-third-party.ts 1`] = ` +exports[`imports-with-comments-and-third-party.ts - typescript-verify > imports-with-comments-and-third-party.ts 1`] = ` // I am top level comment in this file. // I am second line of top level comment in this file. import './commands'; @@ -132,7 +132,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-comments-on-top.ts - typescript-verify: imports-with-comments-on-top.ts 1`] = ` +exports[`imports-with-comments-on-top.ts - typescript-verify > imports-with-comments-on-top.ts 1`] = ` // I am top level comment in this file. // I am second line of top level comment in this file. import z from 'z'; @@ -179,7 +179,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-directives.ts - typescript-verify: imports-with-directives.ts 1`] = ` +exports[`imports-with-directives.ts - typescript-verify > imports-with-directives.ts 1`] = ` 'use strict'; 'use client'; @@ -244,7 +244,7 @@ const workletAdd = (a: number, b: number) => { `; -exports[`imports-with-file-level-comments.ts - typescript-verify: imports-with-file-level-comments.ts 1`] = ` +exports[`imports-with-file-level-comments.ts - typescript-verify > imports-with-file-level-comments.ts 1`] = ` //@ts-ignore // I am file top level comments import threeLevelRelativePath from "../../../threeLevelRelativePath"; @@ -321,7 +321,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-interpreter-directive.ts - typescript-verify: imports-with-interpreter-directive.ts 1`] = ` +exports[`imports-with-interpreter-directive.ts - typescript-verify > imports-with-interpreter-directive.ts 1`] = ` #!/usr/bin/env node import otherthing from "@core/otherthing"; import abc from "@core/abc"; @@ -355,7 +355,7 @@ function add(a: number, b: number) { `; -exports[`imports-without-third-party.ts - typescript-verify: imports-without-third-party.ts 1`] = ` +exports[`imports-without-third-party.ts - typescript-verify > imports-without-third-party.ts 1`] = ` // I am top level comment import otherthing from "@core/otherthing"; import abc from "@core/abc"; @@ -379,7 +379,7 @@ import twoLevelRelativePath from "../../twoLevelRelativePath"; `; -exports[`no-import-export.ts - typescript-verify: no-import-export.ts 1`] = ` +exports[`no-import-export.ts - typescript-verify > no-import-export.ts 1`] = ` function add(a:number,b:number) { return a + b; } @@ -390,7 +390,7 @@ function add(a: number, b: number) { `; -exports[`one-import.ts - typescript-verify: one-import.ts 1`] = ` +exports[`one-import.ts - typescript-verify > one-import.ts 1`] = ` // This example support/index.js is processed and // loaded automatically before your test files. // @@ -432,7 +432,7 @@ import "./commands"; `; -exports[`sort-imports-ignored.ts - typescript-verify: sort-imports-ignored.ts 1`] = ` +exports[`sort-imports-ignored.ts - typescript-verify > sort-imports-ignored.ts 1`] = ` // sort-imports-ignore import './commands'; diff --git a/tests/ImportsSeparated/ppsi.spec.js b/tests/ImportsSeparated/ppsi.spec.mjs similarity index 100% rename from tests/ImportsSeparated/ppsi.spec.js rename to tests/ImportsSeparated/ppsi.spec.mjs diff --git a/tests/ImportsSeparatedByUser/__snapshots__/ppsi.spec.mjs.snap b/tests/ImportsSeparatedByUser/__snapshots__/ppsi.spec.mjs.snap new file mode 100644 index 00000000..dc904a1c --- /dev/null +++ b/tests/ImportsSeparatedByUser/__snapshots__/ppsi.spec.mjs.snap @@ -0,0 +1,446 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`import-export-in-between.ts - typescript-verify > import-export-in-between.ts 1`] = ` +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; +import thirdParty from "third-party"; +export { random } from './random'; +import c from 'c'; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import otherthing from "@core/otherthing"; +import a from 'a'; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +export default { + title: 'hello', +}; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +import x from 'x'; + +function add(a:number,b:number) { + return a + b; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import a from "a"; +import c from "c"; +import thirdParty from "third-party"; +import x from "x"; +import otherthing from "@core/otherthing"; +import something from "@server/something"; + +import component from "@ui/hello"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; + +export { random } from "./random"; + +export default { + title: "hello", +}; + +function add(a: number, b: number) { + return a + b; +} + +`; + +exports[`import-export-only.ts - typescript-verify > import-export-only.ts 1`] = ` +import React from 'react'; +export const a = 1; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import React from "react"; + +export const a = 1; + +`; + +exports[`import-with-type-imports-together.ts - typescript-verify > import-with-type-imports-together.ts 1`] = ` +import { foo } from "@server/foo" +import type { Quux } from "./quux" +import { Link } from "@ui/Link" +import type { Bar } from "@server/bar" +import type { LinkProps } from "@ui/Link/LinkProps" +import { baz } from "./baz" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import type { Bar } from "@server/bar"; +import { foo } from "@server/foo"; + +import { Link } from "@ui/Link"; +import type { LinkProps } from "@ui/Link/LinkProps"; +import { baz } from "./baz"; +import type { Quux } from "./quux"; + +`; + +exports[`imports-with-comments.ts - typescript-verify > imports-with-comments.ts 1`] = ` +// I am top level comment in this file. +// I am second line of top level comment in this file. +import './commands'; + +// Comment +// Comment + +function add(a:number,b:number) { + return a + b; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// I am top level comment in this file. +// I am second line of top level comment in this file. +import "./commands"; + +// Comment +// Comment + +function add(a: number, b: number) { + return a + b; +} + +`; + +exports[`imports-with-comments-and-third-party.ts - typescript-verify > imports-with-comments-and-third-party.ts 1`] = ` +// I am top level comment in this file. +// I am second line of top level comment in this file. +import './commands'; +import React from 'react'; +// Comment +// Comment + +function add(a:number,b:number) { + return a + b; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// I am top level comment in this file. +// I am second line of top level comment in this file. +import React from "react"; + +import "./commands"; + +// Comment +// Comment + +function add(a: number, b: number) { + return a + b; +} + +`; + +exports[`imports-with-comments-on-top.ts - typescript-verify > imports-with-comments-on-top.ts 1`] = ` +// I am top level comment in this file. +// I am second line of top level comment in this file. +import z from 'z'; +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; +import thirdParty from "third-party"; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import otherthing from "@core/otherthing"; +import abc from "@core/abc"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +import xyz from "@ui/xyz"; +import qwerty from "@server/qwerty"; + +function add(a:number,b:number) { + return a + b; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// I am top level comment in this file. +// I am second line of top level comment in this file. +import thirdParty from "third-party"; +import z from "z"; +import abc from "@core/abc"; +import otherthing from "@core/otherthing"; +import qwerty from "@server/qwerty"; +import something from "@server/something"; + +import component from "@ui/hello"; +import xyz from "@ui/xyz"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; + +function add(a: number, b: number) { + return a + b; +} + +`; + +exports[`imports-with-directives.ts - typescript-verify > imports-with-directives.ts 1`] = ` +'use strict'; +'use client'; + +// comment after directives +import otherthing from "@core/otherthing"; +import abc from "@core/abc"; + +// Comment + +function add(a:number,b:number) { + return a + b; +} + +function addStrict(a:number,b:number) { + 'use strict'; + return a + b; +} + +'preserve me'; + +const workletAdd = (a:number,b:number) => { + 'worklet'; + return a + b; +} + +(function() { + 'use strict'; + // some iffe example + return true; +})(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"use strict"; +"use client"; + +// comment after directives +import abc from "@core/abc"; +import otherthing from "@core/otherthing"; + +// Comment + +function add(a: number, b: number) { + return a + b; +} + +function addStrict(a: number, b: number) { + "use strict"; + return a + b; +} + +("preserve me"); + +const workletAdd = (a: number, b: number) => { + "worklet"; + return a + b; +}; + +(function () { + "use strict"; + // some iffe example + return true; +})(); + +`; + +exports[`imports-with-file-level-comments.ts - typescript-verify > imports-with-file-level-comments.ts 1`] = ` +//@ts-ignore +// I am file top level comments +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +// I am stick to sameLevelRelativePath +import sameLevelRelativePath from "./sameLevelRelativePath"; +// I am stick to third party comment +import thirdParty from "third-party"; +// leading comment +import { + random // inner comment +} from './random'; +// leading comment +export { + random // inner comment +} from './random'; +import c from 'c'; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import otherthing from "@core/otherthing"; +import a from 'a'; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +export default { + title: 'hello', +}; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +import x from 'x'; + +// I am function comment + +function add(a:number,b:number) { + return a + b; // I am inside function +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +//@ts-ignore +// I am file top level comments +import a from "a"; +import c from "c"; +// I am stick to third party comment +import thirdParty from "third-party"; +import x from "x"; +import otherthing from "@core/otherthing"; +import something from "@server/something"; + +import component from "@ui/hello"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import oneLevelRelativePath from "../oneLevelRelativePath"; +// leading comment +import { + random, // inner comment +} from "./random"; +// I am stick to sameLevelRelativePath +import sameLevelRelativePath from "./sameLevelRelativePath"; + +// leading comment +export { + random, // inner comment +} from "./random"; + +export default { + title: "hello", +}; + +// I am function comment + +function add(a: number, b: number) { + return a + b; // I am inside function +} + +`; + +exports[`imports-with-interpreter-directive.ts - typescript-verify > imports-with-interpreter-directive.ts 1`] = ` +#!/usr/bin/env node +import otherthing from "@core/otherthing"; +import abc from "@core/abc"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +import xyz from "@ui/xyz"; +import qwerty from "@server/qwerty"; + +function add(a:number,b:number) { + return a + b; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#!/usr/bin/env node +import abc from "@core/abc"; +import otherthing from "@core/otherthing"; +import qwerty from "@server/qwerty"; +import something from "@server/something"; + +import component from "@ui/hello"; +import xyz from "@ui/xyz"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; + +function add(a: number, b: number) { + return a + b; +} + +`; + +exports[`imports-without-third-party.ts - typescript-verify > imports-without-third-party.ts 1`] = ` +// I am top level comment +import otherthing from "@core/otherthing"; +import abc from "@core/abc"; +// I am comment +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// I am top level comment +import abc from "@core/abc"; +import otherthing from "@core/otherthing"; +import something from "@server/something"; + +import component from "@ui/hello"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +// I am comment +import twoLevelRelativePath from "../../twoLevelRelativePath"; + +`; + +exports[`no-import-export.ts - typescript-verify > no-import-export.ts 1`] = ` +function add(a:number,b:number) { + return a + b; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function add(a: number, b: number) { + return a + b; +} + +`; + +exports[`one-import.ts - typescript-verify > one-import.ts 1`] = ` +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; + +// Alternatively you can use CommonJS syntax: +// require('./commands') +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** +// Import commands.js using ES2015 syntax: +import "./commands"; + +// Alternatively you can use CommonJS syntax: +// require('./commands') + +`; + +exports[`sort-imports-ignored.ts - typescript-verify > sort-imports-ignored.ts 1`] = ` +// sort-imports-ignore + +import './commands'; +import b from 'b'; +import a from 'a'; + +// Comment + +function add(a:number,b:number) { + return a + b; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// sort-imports-ignore + +import "./commands"; +import b from "b"; +import a from "a"; + +// Comment + +function add(a: number, b: number) { + return a + b; +} + +`; diff --git a/tests/ImportsSeparatedByUser/import-export-in-between.ts b/tests/ImportsSeparatedByUser/import-export-in-between.ts new file mode 100644 index 00000000..16bc1d77 --- /dev/null +++ b/tests/ImportsSeparatedByUser/import-export-in-between.ts @@ -0,0 +1,20 @@ +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; +import thirdParty from "third-party"; +export { random } from './random'; +import c from 'c'; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import otherthing from "@core/otherthing"; +import a from 'a'; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +export default { + title: 'hello', +}; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +import x from 'x'; + +function add(a:number,b:number) { + return a + b; +} diff --git a/tests/ImportsSeparatedByUser/import-export-only.ts b/tests/ImportsSeparatedByUser/import-export-only.ts new file mode 100644 index 00000000..44f7eff4 --- /dev/null +++ b/tests/ImportsSeparatedByUser/import-export-only.ts @@ -0,0 +1,2 @@ +import React from 'react'; +export const a = 1; diff --git a/tests/ImportsSeparatedByUser/import-with-type-imports-together.ts b/tests/ImportsSeparatedByUser/import-with-type-imports-together.ts new file mode 100644 index 00000000..b6d28272 --- /dev/null +++ b/tests/ImportsSeparatedByUser/import-with-type-imports-together.ts @@ -0,0 +1,6 @@ +import { foo } from "@server/foo" +import type { Quux } from "./quux" +import { Link } from "@ui/Link" +import type { Bar } from "@server/bar" +import type { LinkProps } from "@ui/Link/LinkProps" +import { baz } from "./baz" diff --git a/tests/ImportsSeparatedByUser/imports-with-comments-and-third-party.ts b/tests/ImportsSeparatedByUser/imports-with-comments-and-third-party.ts new file mode 100644 index 00000000..a9fb311d --- /dev/null +++ b/tests/ImportsSeparatedByUser/imports-with-comments-and-third-party.ts @@ -0,0 +1,10 @@ +// I am top level comment in this file. +// I am second line of top level comment in this file. +import './commands'; +import React from 'react'; +// Comment +// Comment + +function add(a:number,b:number) { + return a + b; +} diff --git a/tests/ImportsSeparatedByUser/imports-with-comments-on-top.ts b/tests/ImportsSeparatedByUser/imports-with-comments-on-top.ts new file mode 100644 index 00000000..6b08ed06 --- /dev/null +++ b/tests/ImportsSeparatedByUser/imports-with-comments-on-top.ts @@ -0,0 +1,19 @@ +// I am top level comment in this file. +// I am second line of top level comment in this file. +import z from 'z'; +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +import sameLevelRelativePath from "./sameLevelRelativePath"; +import thirdParty from "third-party"; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import otherthing from "@core/otherthing"; +import abc from "@core/abc"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +import xyz from "@ui/xyz"; +import qwerty from "@server/qwerty"; + +function add(a:number,b:number) { + return a + b; +} diff --git a/tests/ImportsSeparatedByUser/imports-with-comments.ts b/tests/ImportsSeparatedByUser/imports-with-comments.ts new file mode 100644 index 00000000..67b8e461 --- /dev/null +++ b/tests/ImportsSeparatedByUser/imports-with-comments.ts @@ -0,0 +1,10 @@ +// I am top level comment in this file. +// I am second line of top level comment in this file. +import './commands'; + +// Comment +// Comment + +function add(a:number,b:number) { + return a + b; +} diff --git a/tests/ImportsSeparatedByUser/imports-with-directives.ts b/tests/ImportsSeparatedByUser/imports-with-directives.ts new file mode 100644 index 00000000..221ec608 --- /dev/null +++ b/tests/ImportsSeparatedByUser/imports-with-directives.ts @@ -0,0 +1,30 @@ +'use strict'; +'use client'; + +// comment after directives +import otherthing from "@core/otherthing"; +import abc from "@core/abc"; + +// Comment + +function add(a:number,b:number) { + return a + b; +} + +function addStrict(a:number,b:number) { + 'use strict'; + return a + b; +} + +'preserve me'; + +const workletAdd = (a:number,b:number) => { + 'worklet'; + return a + b; +} + +(function() { + 'use strict'; + // some iffe example + return true; +})(); diff --git a/tests/ImportsSeparatedByUser/imports-with-file-level-comments.ts b/tests/ImportsSeparatedByUser/imports-with-file-level-comments.ts new file mode 100644 index 00000000..8ffcfb5d --- /dev/null +++ b/tests/ImportsSeparatedByUser/imports-with-file-level-comments.ts @@ -0,0 +1,33 @@ +//@ts-ignore +// I am file top level comments +import threeLevelRelativePath from "../../../threeLevelRelativePath"; +// I am stick to sameLevelRelativePath +import sameLevelRelativePath from "./sameLevelRelativePath"; +// I am stick to third party comment +import thirdParty from "third-party"; +// leading comment +import { + random // inner comment +} from './random'; +// leading comment +export { + random // inner comment +} from './random'; +import c from 'c'; +import oneLevelRelativePath from "../oneLevelRelativePath"; +import otherthing from "@core/otherthing"; +import a from 'a'; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +export default { + title: 'hello', +}; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +import x from 'x'; + +// I am function comment + +function add(a:number,b:number) { + return a + b; // I am inside function +} diff --git a/tests/ImportsSeparatedByUser/imports-with-interpreter-directive.ts b/tests/ImportsSeparatedByUser/imports-with-interpreter-directive.ts new file mode 100644 index 00000000..8e7de5f6 --- /dev/null +++ b/tests/ImportsSeparatedByUser/imports-with-interpreter-directive.ts @@ -0,0 +1,13 @@ +#!/usr/bin/env node +import otherthing from "@core/otherthing"; +import abc from "@core/abc"; +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; +import xyz from "@ui/xyz"; +import qwerty from "@server/qwerty"; + +function add(a:number,b:number) { + return a + b; +} diff --git a/tests/ImportsSeparatedByUser/imports-without-third-party.ts b/tests/ImportsSeparatedByUser/imports-without-third-party.ts new file mode 100644 index 00000000..ef1797a2 --- /dev/null +++ b/tests/ImportsSeparatedByUser/imports-without-third-party.ts @@ -0,0 +1,8 @@ +// I am top level comment +import otherthing from "@core/otherthing"; +import abc from "@core/abc"; +// I am comment +import twoLevelRelativePath from "../../twoLevelRelativePath"; +import component from "@ui/hello"; +import fourLevelRelativePath from "../../../../fourLevelRelativePath"; +import something from "@server/something"; diff --git a/tests/ImportsSeparatedByUser/no-import-export.ts b/tests/ImportsSeparatedByUser/no-import-export.ts new file mode 100644 index 00000000..e8f71db3 --- /dev/null +++ b/tests/ImportsSeparatedByUser/no-import-export.ts @@ -0,0 +1,3 @@ +function add(a:number,b:number) { + return a + b; +} diff --git a/tests/ImportsSeparatedByUser/one-import.ts b/tests/ImportsSeparatedByUser/one-import.ts new file mode 100644 index 00000000..35d75ba6 --- /dev/null +++ b/tests/ImportsSeparatedByUser/one-import.ts @@ -0,0 +1,19 @@ +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/tests/ImportsSeparatedByUser/ppsi.spec.mjs b/tests/ImportsSeparatedByUser/ppsi.spec.mjs new file mode 100644 index 00000000..3f674010 --- /dev/null +++ b/tests/ImportsSeparatedByUser/ppsi.spec.mjs @@ -0,0 +1,5 @@ +run_spec(__dirname, ["typescript"], { + importOrder: ['^@core/(.*)$', '^@server/(.*)', '', '^@ui/(.*)$', '^[./]'], + importOrderSeparation: true, + importOrderParserPlugins: ['typescript'] +}); diff --git a/tests/ImportsSeparatedByUser/sort-imports-ignored.ts b/tests/ImportsSeparatedByUser/sort-imports-ignored.ts new file mode 100644 index 00000000..c53ddcf0 --- /dev/null +++ b/tests/ImportsSeparatedByUser/sort-imports-ignored.ts @@ -0,0 +1,11 @@ +// sort-imports-ignore + +import './commands'; +import b from 'b'; +import a from 'a'; + +// Comment + +function add(a:number,b:number) { + return a + b; +} diff --git a/tests/ImportsSeparatedRest/__snapshots__/ppsi.spec.js.snap b/tests/ImportsSeparatedRest/__snapshots__/ppsi.spec.mjs.snap similarity index 91% rename from tests/ImportsSeparatedRest/__snapshots__/ppsi.spec.js.snap rename to tests/ImportsSeparatedRest/__snapshots__/ppsi.spec.mjs.snap index b9a6b680..b9842cbf 100644 --- a/tests/ImportsSeparatedRest/__snapshots__/ppsi.spec.js.snap +++ b/tests/ImportsSeparatedRest/__snapshots__/ppsi.spec.mjs.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`import-export-in-between.ts - typescript-verify: import-export-in-between.ts 1`] = ` +exports[`import-export-in-between.ts - typescript-verify > import-export-in-between.ts 1`] = ` import threeLevelRelativePath from "../../../threeLevelRelativePath"; import sameLevelRelativePath from "./sameLevelRelativePath"; import thirdParty from "third-party"; @@ -51,7 +51,7 @@ function add(a: number, b: number) { `; -exports[`import-export-only.ts - typescript-verify: import-export-only.ts 1`] = ` +exports[`import-export-only.ts - typescript-verify > import-export-only.ts 1`] = ` import React from 'react'; export const a = 1; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -61,7 +61,7 @@ export const a = 1; `; -exports[`imports-with-comments.ts - typescript-verify: imports-with-comments.ts 1`] = ` +exports[`imports-with-comments.ts - typescript-verify > imports-with-comments.ts 1`] = ` // I am top level comment in this file. // I am second line of top level comment in this file. import './commands'; @@ -86,7 +86,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-comments-and-third-party.ts - typescript-verify: imports-with-comments-and-third-party.ts 1`] = ` +exports[`imports-with-comments-and-third-party.ts - typescript-verify > imports-with-comments-and-third-party.ts 1`] = ` // I am top level comment in this file. // I am second line of top level comment in this file. import './commands'; @@ -113,7 +113,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-comments-on-top.ts - typescript-verify: imports-with-comments-on-top.ts 1`] = ` +exports[`imports-with-comments-on-top.ts - typescript-verify > imports-with-comments-on-top.ts 1`] = ` // I am top level comment in this file. // I am second line of top level comment in this file. import z from 'z'; @@ -160,7 +160,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-file-level-comments.ts - typescript-verify: imports-with-file-level-comments.ts 1`] = ` +exports[`imports-with-file-level-comments.ts - typescript-verify > imports-with-file-level-comments.ts 1`] = ` //@ts-ignore // I am file top level comments import threeLevelRelativePath from "../../../threeLevelRelativePath"; @@ -237,7 +237,7 @@ function add(a: number, b: number) { `; -exports[`imports-with-interpreter-directive.ts - typescript-verify: imports-with-interpreter-directive.ts 1`] = ` +exports[`imports-with-interpreter-directive.ts - typescript-verify > imports-with-interpreter-directive.ts 1`] = ` #!/usr/bin/env node import otherthing from "@core/otherthing"; import abc from "@core/abc"; @@ -271,7 +271,7 @@ function add(a: number, b: number) { `; -exports[`imports-without-third-party.ts - typescript-verify: imports-without-third-party.ts 1`] = ` +exports[`imports-without-third-party.ts - typescript-verify > imports-without-third-party.ts 1`] = ` // I am top level comment import otherthing from "@core/otherthing"; import abc from "@core/abc"; @@ -295,7 +295,7 @@ import twoLevelRelativePath from "../../twoLevelRelativePath"; `; -exports[`no-import-export.ts - typescript-verify: no-import-export.ts 1`] = ` +exports[`no-import-export.ts - typescript-verify > no-import-export.ts 1`] = ` function add(a:number,b:number) { return a + b; } @@ -306,7 +306,7 @@ function add(a: number, b: number) { `; -exports[`one-import.ts - typescript-verify: one-import.ts 1`] = ` +exports[`one-import.ts - typescript-verify > one-import.ts 1`] = ` // This example support/index.js is processed and // loaded automatically before your test files. // @@ -348,7 +348,7 @@ import "./commands"; `; -exports[`sort-imports-ignored.ts - typescript-verify: sort-imports-ignored.ts 1`] = ` +exports[`sort-imports-ignored.ts - typescript-verify > sort-imports-ignored.ts 1`] = ` // sort-imports-ignore import './commands'; diff --git a/tests/ImportsSeparatedRest/ppsi.spec.js b/tests/ImportsSeparatedRest/ppsi.spec.mjs similarity index 100% rename from tests/ImportsSeparatedRest/ppsi.spec.js rename to tests/ImportsSeparatedRest/ppsi.spec.mjs diff --git a/tests/ImportsWithAttributesKeyword/__snapshots__/ppsi.spec.js.snap b/tests/ImportsWithAttributesKeyword/__snapshots__/ppsi.spec.mjs.snap similarity index 85% rename from tests/ImportsWithAttributesKeyword/__snapshots__/ppsi.spec.js.snap rename to tests/ImportsWithAttributesKeyword/__snapshots__/ppsi.spec.mjs.snap index 7972df24..bb99c506 100644 --- a/tests/ImportsWithAttributesKeyword/__snapshots__/ppsi.spec.js.snap +++ b/tests/ImportsWithAttributesKeyword/__snapshots__/ppsi.spec.mjs.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`imports-with-attributes-keyword.ts - typescript-verify: imports-with-attributes-keyword.ts 1`] = ` +exports[`imports-with-attributes-keyword.ts - typescript-verify > imports-with-attributes-keyword.ts 1`] = ` // I am top level comment in this file. import thirdParty0 from "third-party0"; import something3 from "@core/something3"; diff --git a/tests/ImportsWithAttributesKeyword/ppsi.spec.js b/tests/ImportsWithAttributesKeyword/ppsi.spec.mjs similarity index 100% rename from tests/ImportsWithAttributesKeyword/ppsi.spec.js rename to tests/ImportsWithAttributesKeyword/ppsi.spec.mjs diff --git a/tests/Svelte/__snapshots__/ppsi.spec.js.snap b/tests/Svelte/__snapshots__/ppsi.spec.mjs.snap similarity index 97% rename from tests/Svelte/__snapshots__/ppsi.spec.js.snap rename to tests/Svelte/__snapshots__/ppsi.spec.mjs.snap index 382c31fe..1010ebba 100644 --- a/tests/Svelte/__snapshots__/ppsi.spec.js.snap +++ b/tests/Svelte/__snapshots__/ppsi.spec.mjs.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`module.svelte - svelte-verify: module.svelte 1`] = ` +exports[`module.svelte - svelte-verify > module.svelte 1`] = `