├── .envrc
├── .gitignore
├── src
├── index.js
└── jscodeshift-mode.js
├── tests
├── transforms
│ ├── preserves-style-spacing
│ │ ├── transform.js
│ │ └── transform.test.js
│ ├── non-top-level-tags
│ │ ├── transform.js
│ │ └── transform.test.js
│ ├── preserves-template-spacing
│ │ ├── transform.js
│ │ └── transform.test.js
│ ├── no-script-block
│ │ ├── transform.js
│ │ └── transform.test.js
│ ├── processing-js-file
│ │ ├── transform.js
│ │ └── transform.test.js
│ ├── script-setup-block
│ │ ├── transform.js
│ │ └── transform.test.js
│ ├── preserves-script-spacing
│ │ ├── transform.js
│ │ └── transform.test.js
│ ├── script-setup-and-script-blocks
│ │ ├── transform.js
│ │ └── transform.test.js
│ ├── two-scripts-only-script-changed
│ │ ├── transform.js
│ │ └── transform.test.js
│ ├── two-scripts-only-script-setup-changed
│ │ ├── transform.js
│ │ └── transform.test.js
│ ├── handles-whitespace-lines
│ │ ├── transform.js
│ │ └── transform.test.js
│ ├── test-helper.js
│ └── codemod-example
│ │ ├── transform.js
│ │ └── transform.test.js
├── make-sfc.js
└── jscodeshift-mode.test.js
├── .travis.yml
├── flake.nix
├── LICENSE.mit
├── package.json
├── flake.lock
├── README.md
├── ideas.md
└── CHANGELOG.md
/.envrc:
--------------------------------------------------------------------------------
1 | use flake .
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | # nix
4 | .direnv
5 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { jscodeshiftMode } from './jscodeshift-mode.js';
2 |
3 | export default function adapt(transform) {
4 | return jscodeshiftMode(transform);
5 | }
6 |
--------------------------------------------------------------------------------
/tests/transforms/preserves-style-spacing/transform.js:
--------------------------------------------------------------------------------
1 | import adapt from '../../../src/index.js';
2 |
3 | export default adapt((fileInfo, api, options) => {
4 | return `
5 | export default {};
6 | `;
7 | });
8 |
--------------------------------------------------------------------------------
/tests/transforms/non-top-level-tags/transform.js:
--------------------------------------------------------------------------------
1 | import adapt from '../../../src/index.js';
2 |
3 | export default adapt((fileInfo, api, options) => {
4 | return `
5 | // this is new` + fileInfo.source;
6 | });
7 |
--------------------------------------------------------------------------------
/tests/transforms/preserves-template-spacing/transform.js:
--------------------------------------------------------------------------------
1 | import adapt from '../../../src/index.js';
2 |
3 | export default adapt((fileInfo, api, options) => {
4 | return `
5 | export default {};
6 | `;
7 | });
8 |
--------------------------------------------------------------------------------
/tests/transforms/no-script-block/transform.js:
--------------------------------------------------------------------------------
1 | import adapt from '../../../src/index.js';
2 |
3 | export default adapt((fileInfo, api, options) => {
4 | throw new Error('This transform should not have been invoked');
5 | });
6 |
--------------------------------------------------------------------------------
/tests/transforms/processing-js-file/transform.js:
--------------------------------------------------------------------------------
1 | import adapt from '../../../src/index.js';
2 |
3 | export default adapt((fileInfo, api, options) => {
4 | return `
5 | export function go() {
6 | console.log('going');
7 | }
8 | `;
9 | });
10 |
--------------------------------------------------------------------------------
/tests/transforms/script-setup-block/transform.js:
--------------------------------------------------------------------------------
1 | import adapt from '../../../src/index.js';
2 |
3 | export default adapt((fileInfo, api, options) => {
4 | return api.jscodeshift(fileInfo.source)
5 | .findVariableDeclarators('foo')
6 | .renameTo('bar')
7 | .toSource();
8 | });
9 |
--------------------------------------------------------------------------------
/tests/transforms/preserves-script-spacing/transform.js:
--------------------------------------------------------------------------------
1 | import adapt from '../../../src/index.js';
2 |
3 | export default adapt((fileInfo, api, options) => {
4 | return api.jscodeshift(fileInfo.source)
5 | .findVariableDeclarators('foo')
6 | .renameTo('bar')
7 | .toSource();
8 | });
9 |
--------------------------------------------------------------------------------
/tests/transforms/script-setup-and-script-blocks/transform.js:
--------------------------------------------------------------------------------
1 | import adapt from '../../../src/index.js';
2 |
3 | export default adapt((fileInfo, api, options) => {
4 | return api.jscodeshift(fileInfo.source)
5 | .findVariableDeclarators('foo')
6 | .renameTo('bar')
7 | .toSource();
8 | });
9 |
--------------------------------------------------------------------------------
/tests/transforms/two-scripts-only-script-changed/transform.js:
--------------------------------------------------------------------------------
1 | import adapt from '../../../src/index.js';
2 |
3 | export default adapt((fileInfo, api, options) => {
4 | return api.jscodeshift(fileInfo.source)
5 | .findVariableDeclarators('foo')
6 | .renameTo('bar')
7 | .toSource();
8 | });
9 |
--------------------------------------------------------------------------------
/tests/transforms/two-scripts-only-script-setup-changed/transform.js:
--------------------------------------------------------------------------------
1 | import adapt from '../../../src/index.js';
2 |
3 | export default adapt((fileInfo, api, options) => {
4 | return api.jscodeshift(fileInfo.source)
5 | .findVariableDeclarators('foo')
6 | .renameTo('bar')
7 | .toSource();
8 | });
9 |
--------------------------------------------------------------------------------
/tests/transforms/processing-js-file/transform.test.js:
--------------------------------------------------------------------------------
1 | import { testTransform } from '../test-helper.js';
2 | import transform from './transform.js';
3 |
4 | const input = `
5 | export function stop() {
6 | console.log('stopping');
7 | }
8 | `;
9 |
10 | const output = `
11 | export function go() {
12 | console.log('going');
13 | }
14 | `;
15 |
16 | testTransform(transform, 'util.js', input, output);
17 |
--------------------------------------------------------------------------------
/tests/transforms/no-script-block/transform.test.js:
--------------------------------------------------------------------------------
1 | import { testTransform } from '../test-helper.js';
2 | import transform from './transform.js';
3 |
4 | const input = `
5 |
6 | This is just a template
7 |
8 | `;
9 |
10 | // jscodeshift test utils represent a no-op transform as empty string
11 | const output = '';
12 |
13 | testTransform(transform, 'Widget.vue', input, output);
--------------------------------------------------------------------------------
/tests/transforms/handles-whitespace-lines/transform.js:
--------------------------------------------------------------------------------
1 | import adapt from '../../../src/index.js';
2 |
3 | export default adapt((fileInfo, api, options) => {
4 | const j = api.jscodeshift;
5 |
6 | return j(fileInfo.source)
7 | .find(j.Identifier)
8 | .forEach((path) => {
9 | j(path).replaceWith(
10 | j.identifier(path.node.name.split('').reverse().join(''))
11 | );
12 | })
13 | .toSource();
14 | });
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "20"
4 | - "18"
5 | - "16"
6 | # copied from https://github.com/greenkeeperio/greenkeeper-lockfile
7 | before_install:
8 | # package-lock.json was introduced in npm@5
9 | - '[[ $(node -v) =~ ^v9.*$ ]] || npm install -g npm@latest' # skipped when using node 9
10 | - npm install -g greenkeeper-lockfile
11 | install: npm install
12 | before_script: greenkeeper-lockfile-update
13 | after_script: greenkeeper-lockfile-upload
--------------------------------------------------------------------------------
/tests/make-sfc.js:
--------------------------------------------------------------------------------
1 | export default ({ template, script, scriptSetup, style }) => {
2 | const source = [
3 | template ? `${template}` : '',
4 | script ? `` : '',
5 | scriptSetup ? `` : '',
6 | style ? `` : '',
7 | ]
8 | .filter(content => content)
9 | .join('\n\n');
10 |
11 | return '\n' + source + '\n';
12 | };
13 |
--------------------------------------------------------------------------------
/tests/transforms/preserves-style-spacing/transform.test.js:
--------------------------------------------------------------------------------
1 | import { testTransform } from '../test-helper.js';
2 | import transform from './transform.js';
3 |
4 | const input = `
5 |
10 |
11 |
16 | `;
17 |
18 | const output = `
19 |
22 |
23 |
28 | `;
29 |
30 | testTransform(transform, 'Widget.vue', input, output);
--------------------------------------------------------------------------------
/tests/transforms/preserves-script-spacing/transform.test.js:
--------------------------------------------------------------------------------
1 | import { testTransform } from '../test-helper.js';
2 | import transform from './transform.js';
3 |
4 | const input = `
5 |
6 |
7 |
8 |
14 | `;
15 |
16 | const output = `
17 |
18 |
19 |
20 |
26 | `;
27 |
28 | testTransform(transform, 'Widget.vue', input, output);
--------------------------------------------------------------------------------
/tests/transforms/test-helper.js:
--------------------------------------------------------------------------------
1 | import { runInlineTest } from 'jscodeshift/dist/testUtils';
2 |
3 | /**
4 | * Test a transform function.
5 | *
6 | * @param {Function} transform
7 | * @param {String} path - (fake) file path of source passed to transform
8 | * @param {String} input - Source code
9 | * @param {String} output - Expected transform result
10 | * @param {Object} options - Options passed to transform, optional
11 | */
12 | export function testTransform(transform, path, input, output, options = {}) {
13 | const fileInfo = {
14 | path,
15 | source: input
16 | };
17 |
18 | it('transforms correctly', () => {
19 | runInlineTest(transform, options, fileInfo, output);
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/tests/transforms/script-setup-block/transform.test.js:
--------------------------------------------------------------------------------
1 | import { testTransform } from '../test-helper.js';
2 | import transform from './transform.js';
3 |
4 | const input = `
5 |
6 |
7 | Hello {{name}}
8 |
9 |
10 |
11 |
14 |
15 |
20 | `;
21 |
22 | const output = `
23 |
24 |
25 | Hello {{name}}
26 |
27 |
28 |
29 |
32 |
33 |
38 | `;
39 |
40 | testTransform(transform, 'Widget.vue', input, output);
--------------------------------------------------------------------------------
/tests/transforms/script-setup-and-script-blocks/transform.test.js:
--------------------------------------------------------------------------------
1 | import { testTransform } from '../test-helper.js';
2 | import transform from './transform.js';
3 |
4 | const input = `
5 |
6 |
7 | Hello {{name}}
8 |
9 |
10 |
11 |
14 |
15 |
18 |
19 |
24 | `;
25 |
26 | const output = `
27 |
28 |
29 | Hello {{name}}
30 |
31 |
32 |
33 |
36 |
37 |
40 |
41 |
46 | `;
47 |
48 | testTransform(transform, 'Widget.vue', input, output);
--------------------------------------------------------------------------------
/tests/transforms/preserves-template-spacing/transform.test.js:
--------------------------------------------------------------------------------
1 | import { testTransform } from '../test-helper.js';
2 | import transform from './transform.js';
3 |
4 | const input = `
5 |
6 |
10 | Hello {{name}}
11 |
12 |
13 |
14 |
24 |
25 |
30 | `;
31 |
32 | const output = `
33 |
34 |
38 | Hello {{name}}
39 |
40 |
41 |
42 |
45 |
46 |
51 | `;
52 |
53 | testTransform(transform, 'Widget.vue', input, output);
--------------------------------------------------------------------------------
/tests/transforms/two-scripts-only-script-changed/transform.test.js:
--------------------------------------------------------------------------------
1 | import { testTransform } from '../test-helper.js';
2 | import transform from './transform.js';
3 |
4 | const input = `
5 |
6 |
7 | Hello {{name}}
8 |
9 |
10 |
11 |
15 |
16 |
20 |
21 |
26 | `;
27 |
28 | const output = `
29 |
30 |
31 | Hello {{name}}
32 |
33 |
34 |
35 |
39 |
40 |
44 |
45 |
50 | `;
51 |
52 | testTransform(transform, 'Widget.vue', input, output);
--------------------------------------------------------------------------------
/tests/transforms/two-scripts-only-script-setup-changed/transform.test.js:
--------------------------------------------------------------------------------
1 | import { testTransform } from '../test-helper.js';
2 | import transform from './transform.js';
3 |
4 | const input = `
5 |
6 |
7 | Hello {{name}}
8 |
9 |
10 |
11 |
15 |
16 |
20 |
21 |
26 | `;
27 |
28 | const output = `
29 |
30 |
31 | Hello {{name}}
32 |
33 |
34 |
35 |
39 |
40 |
44 |
45 |
50 | `;
51 |
52 | testTransform(transform, 'Widget.vue', input, output);
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
4 | systems.url = "github:nix-systems/default";
5 | };
6 |
7 | outputs = {
8 | systems,
9 | nixpkgs,
10 | ...
11 | } @ inputs: let
12 | eachSystem = f:
13 | nixpkgs.lib.genAttrs (import systems) (
14 | system:
15 | f nixpkgs.legacyPackages.${system}
16 | );
17 | in {
18 | devShells = eachSystem (pkgs: {
19 | default = pkgs.mkShell {
20 | buildInputs = [
21 | pkgs.nodejs_20
22 | # You can set the major version of Node.js to a specific one instead
23 | # of the default version
24 | # pkgs.nodejs-19_x
25 |
26 | # You can choose pnpm, yarn, or none (npm).
27 | # pkgs.nodePackages.pnpm
28 | # pkgs.yarn
29 |
30 | # pkgs.nodePackages.typescript
31 | # pkgs.nodePackages.typescript-language-server
32 | ];
33 | };
34 | });
35 | };
36 | }
--------------------------------------------------------------------------------
/tests/transforms/codemod-example/transform.js:
--------------------------------------------------------------------------------
1 | import adapt from '../../../src/index.js';
2 |
3 | export default adapt((fileInfo, api, options) => {
4 | const j = api.jscodeshift;
5 | const root = j(fileInfo.source);
6 |
7 | root
8 | .find(j.ExportDefaultDeclaration)
9 | .find(j.ObjectExpression)
10 | .at(0)
11 | .find(j.Property, p => {
12 | return p.key.name === 'props'
13 | })
14 | .find(j.Property)
15 | .filter(p => {
16 | return p.parent.parent.node.key.name === 'props';
17 | })
18 | .find(j.ObjectExpression)
19 | .forEach(o => {
20 | const requiredFlag = o.node.properties.find(p => {
21 | return p.key.name === 'required'
22 | });
23 |
24 | if (requiredFlag) {
25 | if (requiredFlag.value.value === false) {
26 | requiredFlag.value = j.literal(true);
27 | }
28 | } else {
29 | o.node.properties.push(j.property(
30 | 'init',
31 | j.identifier('required'),
32 | j.literal(true)
33 | ));
34 | }
35 | });
36 |
37 | return root.toSource();
38 | });
39 |
--------------------------------------------------------------------------------
/LICENSE.mit:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 Paul Salaets
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | The above copyright notice and this permission notice shall be included in
12 | all copies or substantial portions of the Software.
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/tests/transforms/handles-whitespace-lines/transform.test.js:
--------------------------------------------------------------------------------
1 | import { testTransform } from '../test-helper.js';
2 | import transform from './transform.js';
3 |
4 | const input = `
5 |
6 |
7 |
8 |
9 |
10 |
11 |
26 | `;
27 |
28 | const output = `
29 |
30 |
31 |
32 |
33 |
34 |
35 |
50 | `;
51 |
52 | testTransform(transform, 'Widget.vue', input, output);
53 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-jscodeshift-adapter",
3 | "version": "3.0.0",
4 | "description": "Run jscodeshift on Vue single file components",
5 | "type": "module",
6 | "main": "src/index.js",
7 | "scripts": {
8 | "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
9 | },
10 | "keywords": [
11 | "vue",
12 | "sfc",
13 | "component",
14 | "refactor",
15 | "codemod",
16 | "jscodeshift"
17 | ],
18 | "author": "Paul Salaets ",
19 | "license": "MIT",
20 | "repository": {
21 | "type": "git",
22 | "url": "https://github.com/psalaets/vue-jscodeshift-adapter.git"
23 | },
24 | "homepage": "https://github.com/psalaets/vue-jscodeshift-adapter",
25 | "bugs": {
26 | "url": "https://github.com/psalaets/vue-jscodeshift-adapter/issues"
27 | },
28 | "devDependencies": {
29 | "jest": "^29.7.0",
30 | "jscodeshift": "^0.15.2",
31 | "jscodeshift-helper": "^1.1.0"
32 | },
33 | "dependencies": {
34 | "@vue/compiler-sfc": "^3.4.23",
35 | "vue-sfc-descriptor-to-string": "^3.0.1"
36 | },
37 | "jest": {
38 | "testEnvironment": "node",
39 | "transform": {}
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "nixpkgs": {
4 | "locked": {
5 | "lastModified": 1713254108,
6 | "narHash": "sha256-0TZIsfDbHG5zibtlw6x0yOp3jkInIGaJ35B7Y4G8Pec=",
7 | "owner": "NixOS",
8 | "repo": "nixpkgs",
9 | "rev": "2fd19c8be2551a61c1ddc3d9f86d748f4db94f00",
10 | "type": "github"
11 | },
12 | "original": {
13 | "owner": "NixOS",
14 | "ref": "nixpkgs-unstable",
15 | "repo": "nixpkgs",
16 | "type": "github"
17 | }
18 | },
19 | "root": {
20 | "inputs": {
21 | "nixpkgs": "nixpkgs",
22 | "systems": "systems"
23 | }
24 | },
25 | "systems": {
26 | "locked": {
27 | "lastModified": 1681028828,
28 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
29 | "owner": "nix-systems",
30 | "repo": "default",
31 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
32 | "type": "github"
33 | },
34 | "original": {
35 | "owner": "nix-systems",
36 | "repo": "default",
37 | "type": "github"
38 | }
39 | }
40 | },
41 | "root": "root",
42 | "version": 7
43 | }
44 |
--------------------------------------------------------------------------------
/tests/transforms/non-top-level-tags/transform.test.js:
--------------------------------------------------------------------------------
1 | import { testTransform } from '../test-helper.js';
2 | import transform from './transform.js';
3 |
4 | const input = `
5 |
6 |
14 |
15 |
16 |
26 |
27 |
32 | `;
33 |
34 | const output = `
35 |
36 |
44 |
45 |
46 |
57 |
58 |
63 | `;
64 |
65 | testTransform(transform, 'Widget.vue', input, output);
--------------------------------------------------------------------------------
/tests/transforms/codemod-example/transform.test.js:
--------------------------------------------------------------------------------
1 | import { testTransform } from '../test-helper.js';
2 | import transform from './transform.js';
3 |
4 | const input = `
5 |
6 |
7 | Hello {{name}}
8 |
9 |
10 |
11 |
33 |
34 |
39 | `;
40 |
41 | const output = `
42 |
43 |
44 | Hello {{name}}
45 |
46 |
47 |
48 |
71 |
72 |
77 | `;
78 |
79 | testTransform(transform, 'Widget.vue', input, output);
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-jscodeshift-adapter
2 |
3 | [](https://travis-ci.org/psalaets/vue-jscodeshift-adapter)
4 |
5 | Run [jscodeshift](https://github.com/facebook/jscodeshift) on Vue single file components
6 |
7 | ## Install
8 |
9 | ```
10 | npm install vue-jscodeshift-adapter -D
11 | ```
12 |
13 | ## Usage
14 |
15 | The instructions below assume you're familiar with [jscodeshift](https://github.com/facebook/jscodeshift).
16 |
17 | ### Run a codemod on some `.js` and/or `.vue` files
18 |
19 | |When transforming|`fileInfo.source` will be|
20 | |-----------------|-------------------------|
21 | |`.js` | the contents of the file|
22 | |`.vue` | the contents of `