├── .gitignore
├── bin
└── kodemod
├── .prettierignore
├── renovate.json
├── .babelrc
├── .eslintignore
├── docs
└── images
│ └── kodemod-cli-screenshot.png
├── transforms
├── __tests__
│ ├── block-scope-to-iife.test.js
│ ├── lit-element-to-lit-imports.test.js
│ ├── rename-tag.test.js
│ └── replace-attrs-test.js
├── __testfixtures__
│ ├── lit-element-to-lit-imports.input.js
│ ├── lit-element-to-lit-imports.output.js
│ ├── block-scope-to-iife.input.js
│ ├── block-scope-to-iife.output.js
│ ├── replace-attrs.input.js
│ ├── replace-attrs.output.js
│ ├── rename-tag.input.js
│ └── rename-tag.output.js
├── block-scope-to-iife.js
├── rename-tag.js
├── replace-attrs.js
└── lit-element-to-lit-imports.js
├── .prettierrc
├── .editorconfig
├── src
├── index.js
├── run-transform.js
└── commands.js
├── .travis.yml
├── .eslintrc.js
├── LICENSE
├── package.json
├── CHANGELOG.md
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | coverage/
3 |
--------------------------------------------------------------------------------
/bin/kodemod:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | require('..');
4 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | CHANGELOG.md
2 | transforms/__testfixtures__/*
3 | coverage/
4 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base",
4 | ":preserveSemverRanges"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"],
3 | "plugins": ["@babel/plugin-proposal-object-rest-spread"]
4 | }
5 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | # config files
2 | .*.js
3 |
4 | # test fixtures
5 | transforms/__testfixtures__/*
6 |
7 | # markdown
8 | *.md
9 |
--------------------------------------------------------------------------------
/docs/images/kodemod-cli-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kcmr/web-components-codemods/HEAD/docs/images/kodemod-cli-screenshot.png
--------------------------------------------------------------------------------
/transforms/__tests__/block-scope-to-iife.test.js:
--------------------------------------------------------------------------------
1 | const { defineTest } = require('jscodeshift/dist/testUtils');
2 |
3 | defineTest(__dirname, 'block-scope-to-iife');
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "semi": true,
4 | "singleQuote": true,
5 | "tabWidth": 2,
6 | "trailingComma": "es5",
7 | "arrowParens": "always"
8 | }
9 |
--------------------------------------------------------------------------------
/transforms/__tests__/lit-element-to-lit-imports.test.js:
--------------------------------------------------------------------------------
1 | const { defineTest } = require('jscodeshift/dist/testUtils');
2 |
3 | defineTest(__dirname, 'lit-element-to-lit-imports');
4 |
--------------------------------------------------------------------------------
/transforms/__tests__/rename-tag.test.js:
--------------------------------------------------------------------------------
1 | const { defineTest } = require('jscodeshift/dist/testUtils');
2 |
3 | defineTest(__dirname, 'rename-tag', {
4 | oldTag: 'some-tag-name',
5 | newTag: 'some-tag-name-renamed',
6 | });
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/lit-element-to-lit-imports.input.js:
--------------------------------------------------------------------------------
1 | import { css } from 'lit-element';
2 | import { LitElement, html, property as foo, customElement } from 'lit-element';
3 | import { repeat } from 'lit-html/directives/repeat.js';
4 | import { ifDefined } from 'lit-html/directives/if-defined';
5 |
--------------------------------------------------------------------------------
/transforms/__tests__/replace-attrs-test.js:
--------------------------------------------------------------------------------
1 | const { defineTest } = require('jscodeshift/dist/testUtils');
2 |
3 | defineTest(__dirname, 'replace-attrs', {
4 | tag: 'wc-icon',
5 | attrs: {
6 | icon: 'emoji',
7 | 'some-attr': 'new-attr',
8 | '.someProp': '.newProp',
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/lit-element-to-lit-imports.output.js:
--------------------------------------------------------------------------------
1 | import { css } from 'lit';
2 | import { LitElement, html } from 'lit';
3 | import { property as foo, customElement } from 'lit/decorators.js';
4 | import { repeat } from 'lit/directives/repeat.js';
5 | import { ifDefined } from 'lit/directives/if-defined';
6 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/block-scope-to-iife.input.js:
--------------------------------------------------------------------------------
1 | {
2 | const { Element, html } = window.Polymer;
3 |
4 | class Component extends Element {
5 | static get is() {
6 | return 'tag-name';
7 | }
8 | }
9 | }
10 |
11 | class Component extends Element {
12 | static get is() {
13 | return 'tag-name';
14 | }
15 | }
16 |
17 | if (true) {
18 | // code
19 | }
20 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/block-scope-to-iife.output.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | const { Element, html } = window.Polymer;
3 |
4 | class Component extends Element {
5 | static get is() {
6 | return 'tag-name';
7 | }
8 | }
9 | })();
10 |
11 | class Component extends Element {
12 | static get is() {
13 | return 'tag-name';
14 | }
15 | }
16 |
17 | if (true) {
18 | // code
19 | }
20 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const { PathPrompt } = require('inquirer-path');
2 | const { CliHelper } = require('@kuscamara/cli-helper');
3 | const commands = require('./commands');
4 |
5 | const cli = new CliHelper({
6 | description: 'Codemods for Web Components',
7 | defaultCommandMessage: 'Choose the transform to apply',
8 | commands,
9 | });
10 |
11 | CliHelper.registerPrompt('path', PathPrompt);
12 | cli.run();
13 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js: stable
3 | dist: trusty
4 |
5 | cache: npm
6 |
7 | before_install:
8 | - npm install -g npm@6
9 | - npm install
10 | - npm install -g codecov
11 |
12 | install:
13 | - npm ci
14 |
15 | script:
16 | - npm t
17 | - codecov
18 |
19 | deploy:
20 | - provider: script
21 | cleanup: true
22 | script:
23 | - npx -p @semantic-release/changelog -p @semantic-release/git -p semantic-release semantic-release
24 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/replace-attrs.input.js:
--------------------------------------------------------------------------------
1 | const expression = 'foo';
2 | const html = (str) => str;
3 |
4 | class Component extends HTMLElement {
5 | get foo() {
6 | return html`
7 |
8 | `;
9 | }
10 |
11 | render() {
12 | return html`
13 |
18 | `;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/replace-attrs.output.js:
--------------------------------------------------------------------------------
1 | const expression = 'foo';
2 | const html = (str) => str;
3 |
4 | class Component extends HTMLElement {
5 | get foo() {
6 | return html`
7 |
8 | `;
9 | }
10 |
11 | render() {
12 | return html`
13 |
18 | `;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/rename-tag.input.js:
--------------------------------------------------------------------------------
1 | const stringLiteral = `
2 |
3 |
4 | `;
5 |
6 | const taggedTemplate = html``;
7 |
8 | const stringMatch = document.querySelector('some-tag-name');
9 | const stringMatch2 = ["\"\\n \\n \""];
10 |
11 | const containsName = ``;
12 |
13 | customElements.define('some-tag-name', SomeTagName);
14 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/rename-tag.output.js:
--------------------------------------------------------------------------------
1 | const stringLiteral = `
2 |
3 |
4 | `;
5 |
6 | const taggedTemplate = html``;
7 |
8 | const stringMatch = document.querySelector('some-tag-name-renamed');
9 | const stringMatch2 = ["\"\\n \\n \""];
10 |
11 | const containsName = ``;
12 |
13 | customElements.define('some-tag-name-renamed', SomeTagName);
14 |
--------------------------------------------------------------------------------
/transforms/block-scope-to-iife.js:
--------------------------------------------------------------------------------
1 | export default function transform(file, api) {
2 | const j = api.jscodeshift;
3 | const isProgramChild = (path) =>
4 | path.parentPath.parentPath.name === 'program';
5 | const iife = (path) =>
6 | j.expressionStatement(
7 | j.callExpression(
8 | j.functionExpression(null, [], j.blockStatement(path.node.body)),
9 | []
10 | )
11 | );
12 |
13 | return j(file.source)
14 | .find(j.BlockStatement)
15 | .filter(isProgramChild)
16 | .replaceWith(iife)
17 | .toSource();
18 | }
19 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: 'babel-eslint',
4 | extends: ['airbnb-base', 'plugin:node/recommended', 'prettier'],
5 | plugins: ['jest', 'node', 'prettier'],
6 | env: {
7 | commonjs: true,
8 | es6: true,
9 | node: true,
10 | },
11 | rules: {
12 | 'no-restricted-syntax': [
13 | 'off',
14 | {
15 | selector: 'ForOfStatement',
16 | },
17 | ],
18 | 'prettier/prettier': 'error',
19 | },
20 | overrides: [
21 | {
22 | files: ['transforms/*.js'],
23 | parserOptions: {
24 | ecmaVersion: 2020,
25 | sourceType: 'module',
26 | },
27 | rules: {
28 | 'node/no-unsupported-features/es-syntax': 'off',
29 | },
30 | },
31 | {
32 | files: ['transforms/__tests__/*.js'],
33 | env: {
34 | 'jest/globals': true,
35 | },
36 | },
37 | ],
38 | };
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Kus Cámara
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/transforms/rename-tag.js:
--------------------------------------------------------------------------------
1 | class TagReplacer {
2 | constructor(parser, oldTag, newTag) {
3 | this.parser = parser;
4 | this.oldTag = oldTag;
5 | this.newTag = newTag;
6 | this.tag = new RegExp(`${oldTag}[^-]`, 'g');
7 | this.hasTag = this.nodeHasTag.bind(this);
8 | this.renamedTag = this.getNewTag.bind(this);
9 | }
10 |
11 | nodeHasTag(path) {
12 | return this.parser(path.node).toSource().match(this.tag);
13 | }
14 |
15 | getNewTag(path) {
16 | const source = this.parser(path).toSource();
17 | return this.replaceTag(source);
18 | }
19 |
20 | replaceTag(source) {
21 | return source.replace(this.tag, (m) => m.replace(this.oldTag, this.newTag));
22 | }
23 | }
24 |
25 | export default function transform(file, api, options) {
26 | const j = api.jscodeshift;
27 | const root = j(file.source);
28 | const { oldTag, newTag, tabWidth = 2, useTabs = false } = options;
29 | const tagReplacer = new TagReplacer(j, oldTag, newTag);
30 | const { hasTag, renamedTag } = tagReplacer;
31 |
32 | root
33 | .find(j.TemplateElement)
34 | .filter(hasTag)
35 | .replaceWith(renamedTag)
36 | .toSource({ tabWidth, useTabs });
37 |
38 | root.find(j.Literal).filter(hasTag).replaceWith(renamedTag);
39 |
40 | return root.toSource({ tabWidth, useTabs });
41 | }
42 |
--------------------------------------------------------------------------------
/src/run-transform.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const execa = require('execa');
3 | const dargs = require('dargs');
4 | const globby = require('globby');
5 |
6 | // eslint-disable-next-line node/no-unpublished-require
7 | const jscodeshift = require.resolve('.bin/jscodeshift');
8 |
9 | // Shamelessly stolen from https://github.com/reactjs/react-codemod/blob/master/bin/cli.js
10 | function expandFilePathsIfNeeded(files) {
11 | const shouldExpandFiles = files.some((file) => file.includes('*'));
12 | return shouldExpandFiles ? globby.sync(files) : files;
13 | }
14 |
15 | module.exports = function runTransform({ command, options }) {
16 | const transformScript = path.join(
17 | __dirname,
18 | '../transforms/',
19 | `${command}.js`
20 | );
21 | const excludes = ['files', 'useTabs'];
22 | const files = expandFilePathsIfNeeded([options.files]);
23 | const args = [
24 | ...files,
25 | `--transform=${transformScript}`,
26 | '--ignore-pattern=**/node_modules/**',
27 | '--ignore-pattern=**/bower_components/**',
28 | ...dargs(options, {
29 | excludes,
30 | allowCamelCase: true,
31 | }),
32 | ];
33 |
34 | // always preview in dry-run mode
35 | if (options.dry) {
36 | args.push('--print');
37 | }
38 |
39 | if (options.useTabs) {
40 | args.push('--useTabs=true');
41 | }
42 |
43 | const result = execa.sync(jscodeshift, args, { stdio: 'inherit' });
44 |
45 | if (result.error) {
46 | throw result.error;
47 | }
48 | };
49 |
--------------------------------------------------------------------------------
/transforms/replace-attrs.js:
--------------------------------------------------------------------------------
1 | class AttrReplacer {
2 | constructor(parser, tag, attrs) {
3 | this.parser = parser;
4 | this.attrs = attrs;
5 | this.tagWithAttrs = new RegExp(`<${tag}([^>]*)>`, 'g');
6 | this.hasTag = this.nodeHasTag.bind(this);
7 | this.tagWithNewAttrs = this.getTagWithNewAttrs.bind(this);
8 | }
9 |
10 | nodeHasTag(path) {
11 | return this.parser(path.node).toSource().match(this.tagWithAttrs);
12 | }
13 |
14 | getTagWithNewAttrs(path) {
15 | const source = this.parser(path).toSource();
16 | return this.replaceAttrs(source);
17 | }
18 |
19 | replaceAttrs(source) {
20 | return source.replace(this.tagWithAttrs, (m) => {
21 | for (const [from, to] of Object.entries(this.attrs)) {
22 | const attribute = new RegExp(`${from}( *=)`, 'g');
23 | // eslint-disable-next-line no-param-reassign
24 | m = m.replace(attribute, (f) => f.replace(from, to));
25 | }
26 |
27 | return m;
28 | });
29 | }
30 | }
31 |
32 | export default function transform(file, api, options) {
33 | const j = api.jscodeshift;
34 | const { tag, attrs, tabWidth = 4, useTabs = false } = options;
35 | const tagAttrReplacer = new AttrReplacer(j, tag, attrs);
36 | const { hasTag, tagWithNewAttrs } = tagAttrReplacer;
37 |
38 | return j(file.source)
39 | .find(j.TaggedTemplateExpression, { tag: { name: 'html' } })
40 | .filter(hasTag)
41 | .replaceWith(tagWithNewAttrs)
42 | .toSource({ tabWidth, useTabs });
43 | }
44 |
--------------------------------------------------------------------------------
/transforms/lit-element-to-lit-imports.js:
--------------------------------------------------------------------------------
1 | export default function transformer(file, api, options) {
2 | const j = api.jscodeshift;
3 | const { quote = 'single' } = options;
4 |
5 | const isLitElementImport = (path) =>
6 | path.value.source.value === 'lit-element';
7 | const isLitHtmlDirectiveImport = (path) =>
8 | path.value.source.value.includes('lit-html/directives/');
9 | const isDecorator = /(customElement|property)/;
10 |
11 | const litImport = j.literal('lit');
12 | const litDirectiveImport = (path) => {
13 | const fullPath = path.value.source.value;
14 | return j.literal(fullPath.replace('lit-html', 'lit'));
15 | };
16 |
17 | const updateDecoratorImports = (path) => {
18 | const decorators = [];
19 | let importNode;
20 |
21 | j(path)
22 | .find(j.ImportSpecifier)
23 | .forEach((specifier) => {
24 | importNode = specifier.parentPath.parentPath;
25 | if (isDecorator.test(specifier.value.imported.name)) {
26 | decorators.push(
27 | j.importSpecifier(specifier.value.imported, specifier.value.local)
28 | );
29 | specifier.replace();
30 | }
31 | });
32 |
33 | if (decorators.length) {
34 | importNode.insertAfter(
35 | j.importDeclaration(decorators, j.literal('lit/decorators.js'))
36 | );
37 | }
38 | };
39 |
40 | const replaceImportLiteral = (path) => ({
41 | with: (value) => j(path).find(j.Literal).replaceWith(value),
42 | });
43 |
44 | return j(file.source)
45 | .find(j.ImportDeclaration)
46 | .forEach((path) => {
47 | if (isLitElementImport(path)) {
48 | replaceImportLiteral(path).with(litImport);
49 | updateDecoratorImports(path);
50 | }
51 |
52 | if (isLitHtmlDirectiveImport(path)) {
53 | replaceImportLiteral(path).with(litDirectiveImport(path));
54 | }
55 | })
56 | .toSource({ quote });
57 | }
58 |
--------------------------------------------------------------------------------
/src/commands.js:
--------------------------------------------------------------------------------
1 | const runTransform = require('./run-transform');
2 |
3 | const commonParams = {
4 | files: {
5 | message: 'Files where the command will be executed',
6 | type: 'path',
7 | validate: () => true,
8 | },
9 | dry: {
10 | message: "Run in preview mode (don't transform files)",
11 | type: 'confirm',
12 | default: false,
13 | },
14 | };
15 |
16 | module.exports = {
17 | 'block-scope-to-iife': {
18 | desc: 'Replaces brackets used as scope in a file by an IIFE',
19 | params: {
20 | ...commonParams,
21 | },
22 | action: runTransform,
23 | },
24 | 'replace-attrs': {
25 | desc: 'Replaces attributes in the specified tag',
26 | params: {
27 | ...commonParams,
28 | tag: {
29 | message: 'Tag name (Example: some-tag)',
30 | type: 'input',
31 | },
32 | attrs: {
33 | message: 'Object with {"old-attr": "new-attr"} pairs to replace',
34 | type: 'input',
35 | },
36 | useTabs: {
37 | message: 'Use tabs instead of spaces',
38 | type: 'confirm',
39 | default: false,
40 | },
41 | tabWidth: {
42 | message: 'Number of spaces used per tab',
43 | type: 'number',
44 | default: 4,
45 | when: (input) => input.useTabs,
46 | },
47 | },
48 | action: runTransform,
49 | },
50 | 'rename-tag': {
51 | desc: 'Renames tag names in strings and template literals',
52 | params: {
53 | ...commonParams,
54 | oldTag: {
55 | message: 'Tag to rename (Example: some-tag)',
56 | type: 'input',
57 | },
58 | newTag: {
59 | message: 'New tag name (Example: new-tag)',
60 | type: 'input',
61 | },
62 | useTabs: {
63 | message: 'Use tabs instead of spaces',
64 | type: 'confirm',
65 | default: false,
66 | },
67 | tabWidth: {
68 | message: 'Number of spaces used per tab',
69 | type: 'number',
70 | default: 2,
71 | when: (input) => input.useTabs,
72 | },
73 | },
74 | action: runTransform,
75 | },
76 | 'lit-element-to-lit-imports': {
77 | desc: 'Replaces lit-element imports to lit imports',
78 | params: {
79 | ...commonParams,
80 | quote: {
81 | message: 'Type of quote (single or double)',
82 | type: 'input',
83 | default: 'single',
84 | },
85 | },
86 | action: runTransform,
87 | },
88 | };
89 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web-components-codemods",
3 | "version": "1.2.3",
4 | "description": "Codemods for Web Components",
5 | "main": "src/index.js",
6 | "keywords": [
7 | "codemods",
8 | "web-components",
9 | "jscodeshift",
10 | "cli"
11 | ],
12 | "author": "Kus Cámara ",
13 | "license": "MIT",
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/kcmr/web-components-codemods.git"
17 | },
18 | "bugs": {
19 | "url": "https://github.com/kcmr/web-components-codemods/issues"
20 | },
21 | "homepage": "https://github.com/kcmr/web-components-codemods#readme",
22 | "scripts": {
23 | "test": "jest",
24 | "lint:eslint": "eslint \"bin/**\" \"src/**\" \"transforms/**\"",
25 | "format:prettier": "prettier --write \"**/*.{js,md}\"",
26 | "format:eslint": "npm run lint:eslint -- --fix",
27 | "format": "npm run format:prettier && npm run format:eslint",
28 | "semantic-release": "semantic-release",
29 | "commit": "git-cz"
30 | },
31 | "bin": {
32 | "kodemod": "./bin/kodemod"
33 | },
34 | "files": [
35 | "bin/",
36 | "src/",
37 | "transforms/*.js"
38 | ],
39 | "dependencies": {
40 | "@kuscamara/cli-helper": "^1.0.5",
41 | "dargs": "^8.0.0",
42 | "execa": "^5.0.0",
43 | "globby": "^11.0.0",
44 | "inquirer-path": "^1.0.0-beta5",
45 | "jscodeshift": "^0.13.0"
46 | },
47 | "devDependencies": {
48 | "@babel/core": "^7.10.3",
49 | "@babel/plugin-proposal-object-rest-spread": "^7.10.3",
50 | "@babel/preset-env": "^7.10.3",
51 | "@commitlint/cli": "^11.0.0",
52 | "@commitlint/config-conventional": "^11.0.0",
53 | "@semantic-release/changelog": "^5.0.1",
54 | "@semantic-release/git": "^9.0.0",
55 | "babel-eslint": "^10.1.0",
56 | "babel-jest": "^27.0.0",
57 | "commitizen": "^4.1.2",
58 | "cz-conventional-changelog": "^3.2.0",
59 | "eslint": "^6.8.0",
60 | "eslint-config-airbnb-base": "^14.0.0",
61 | "eslint-config-prettier": "^7.0.0",
62 | "eslint-plugin-import": "^2.19.1",
63 | "eslint-plugin-jest": "^24.0.0",
64 | "eslint-plugin-node": "^11.0.0",
65 | "eslint-plugin-prettier": "^3.1.2",
66 | "husky": "^4.0.4",
67 | "jest": "^27.0.0",
68 | "lint-staged": "^10.0.3",
69 | "prettier": "^2.0.5",
70 | "semantic-release": "^17.0.0"
71 | },
72 | "husky": {
73 | "hooks": {
74 | "pre-push": "npm t",
75 | "pre-commit": "lint-staged",
76 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
77 | }
78 | },
79 | "lint-staged": {
80 | "*.{js,md}": [
81 | "npm run format"
82 | ]
83 | },
84 | "config": {
85 | "commitizen": {
86 | "path": "cz-conventional-changelog"
87 | }
88 | },
89 | "commitlint": {
90 | "extends": [
91 | "@commitlint/config-conventional"
92 | ]
93 | },
94 | "release": {
95 | "plugins": [
96 | "@semantic-release/commit-analyzer",
97 | "@semantic-release/release-notes-generator",
98 | "@semantic-release/npm",
99 | "@semantic-release/changelog",
100 | "@semantic-release/git"
101 | ],
102 | "branch": "master",
103 | "tagFormat": "${version}"
104 | },
105 | "jest": {
106 | "globals": {
107 | "baseDir": "../"
108 | },
109 | "testEnvironment": "node",
110 | "roots": [
111 | "transforms"
112 | ],
113 | "collectCoverage": true
114 | },
115 | "engines": {
116 | "node": ">=10.13.0"
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [1.2.3](https://github.com/kcmr/web-components-codemods/compare/1.2.2...1.2.3) (2021-10-19)
2 |
3 |
4 | ### Bug Fixes
5 |
6 | * **deps:** update dependency jscodeshift to ^0.13.0 ([5797f30](https://github.com/kcmr/web-components-codemods/commit/5797f30a654c47ce217460a68e8b2d0d77d83299))
7 |
8 | ## [1.2.2](https://github.com/kcmr/web-components-codemods/compare/1.2.1...1.2.2) (2021-10-18)
9 |
10 |
11 | ### Bug Fixes
12 |
13 | * **deps:** update dependency dargs to v8 ([61514b4](https://github.com/kcmr/web-components-codemods/commit/61514b4975f8bb05b4a315faf6ab549192c22d3f))
14 |
15 | ## [1.2.1](https://github.com/kcmr/web-components-codemods/compare/1.2.0...1.2.1) (2021-05-01)
16 |
17 |
18 | ### Bug Fixes
19 |
20 | * **deps:** update dependency jscodeshift to ^0.12.0 ([#59](https://github.com/kcmr/web-components-codemods/issues/59)) ([6029bcc](https://github.com/kcmr/web-components-codemods/commit/6029bcc723f3b7e80d5c6ece3f9196eeb2bc5e8c))
21 |
22 | # [1.2.0](https://github.com/kcmr/web-components-codemods/compare/1.1.4...1.2.0) (2021-04-23)
23 |
24 |
25 | ### Features
26 |
27 | * **transforms:** add lit-element to lit imports codemod ([#58](https://github.com/kcmr/web-components-codemods/issues/58)) ([5152f7e](https://github.com/kcmr/web-components-codemods/commit/5152f7e687cd30a4de353104c3e10e553604b202))
28 |
29 | ## [1.1.4](https://github.com/kcmr/web-components-codemods/compare/1.1.3...1.1.4) (2020-12-31)
30 |
31 |
32 | ### Bug Fixes
33 |
34 | * **deps:** update dependency execa to v5 ([3cf140d](https://github.com/kcmr/web-components-codemods/commit/3cf140d3debbdcd4431e77d7affc6f7cebcc8b34))
35 |
36 | ## [1.1.3](https://github.com/kcmr/web-components-codemods/compare/1.1.2...1.1.3) (2020-11-19)
37 |
38 |
39 | ### Bug Fixes
40 |
41 | * **deps:** update dependency jscodeshift to ^0.11.0 ([3bfe7bd](https://github.com/kcmr/web-components-codemods/commit/3bfe7bddf362e60b78727bf40fb90e00f87bf4ce))
42 |
43 | ## [1.1.2](https://github.com/kcmr/web-components-codemods/compare/1.1.1...1.1.2) (2020-07-07)
44 |
45 |
46 | ### Bug Fixes
47 |
48 | * **deps:** update dependency globby to v11 ([0e8e0ed](https://github.com/kcmr/web-components-codemods/commit/0e8e0edd9ff92f82255e2b3d53a1e2ca07244038))
49 |
50 | ## [1.1.1](https://github.com/kcmr/web-components-codemods/compare/1.1.0...1.1.1) (2020-06-20)
51 |
52 |
53 | ### Bug Fixes
54 |
55 | * **deps:** update dependency jscodeshift to ^0.10.0 ([11a3f77](https://github.com/kcmr/web-components-codemods/commit/11a3f778c3b39918aa33a935c039c88cbbd8c2b0))
56 |
57 | # [1.1.0](https://github.com/kcmr/web-components-codemods/compare/1.0.1...1.1.0) (2020-06-20)
58 |
59 |
60 | ### Bug Fixes
61 |
62 | * **commands:** typo in rename-tag command ([2b8ac06](https://github.com/kcmr/web-components-codemods/commit/2b8ac062fa631c6230c070c81da19f702e3df1a7))
63 | * **transforms:** find tag name in any string ([eb1ee27](https://github.com/kcmr/web-components-codemods/commit/eb1ee27fa8d5ad73c94282f62ef26df647b112d9))
64 |
65 |
66 | ### Features
67 |
68 | * **commands:** add new kodemod ([de74540](https://github.com/kcmr/web-components-codemods/commit/de74540d106c2f2390863bb2a1632b509d00fbb9))
69 | * **transforms:** add a codemod to rename tags ([5b40bde](https://github.com/kcmr/web-components-codemods/commit/5b40bde86865b01981588b5f0f39f6f1131a89cb))
70 | * **transforms:** rename tag name strings ([4e1cdf4](https://github.com/kcmr/web-components-codemods/commit/4e1cdf4e00ac86545a5804bf323b3e96d9cdd94e))
71 |
72 | ## [1.0.1](https://github.com/kcmr/web-components-codemods/compare/1.0.0...1.0.1) (2020-05-03)
73 |
74 |
75 | ### Bug Fixes
76 |
77 | * **package:** update jscodeshift to version 0.8.0 ([ccb1296](https://github.com/kcmr/web-components-codemods/commit/ccb12969093cc9b36ae0dde2600f40653379494f))
78 |
79 | # 1.0.0 (2020-01-05)
80 |
81 |
82 | ### Bug Fixes
83 |
84 | * **package-lock:** remove private registry in some entries ([89694b2](https://github.com/kcmr/web-components-codemods/commit/89694b29b1ee49f77bd04e5b57c1ac5a52087e3f))
85 | * **package-lock:** remove private registry in some entries ([fed7c24](https://github.com/kcmr/web-components-codemods/commit/fed7c24994ded66549d2aba401f0bfadfb4273a9))
86 | * **replace-attrs:** do not replace unmodified nodes ([34a9e2f](https://github.com/kcmr/web-components-codemods/commit/34a9e2fdf8067d4e4f5a6c01e9b3e51b0617e137))
87 | * **replace-attrs:** prevent replacing the tag name or attribute values ([dbeb37e](https://github.com/kcmr/web-components-codemods/commit/dbeb37e7229bf60dc0adb2f47b342cf916fd2ba7))
88 | * **transforms:** prevent replacing brackets in if statement by iife ([f6bf99f](https://github.com/kcmr/web-components-codemods/commit/f6bf99faf5b5a8db58a0a19e4e20a40b7bb7cf63))
89 |
90 |
91 | ### Features
92 |
93 | * **cli:** add CLI to run transforms ([e0f2c82](https://github.com/kcmr/web-components-codemods/commit/e0f2c823976c463792253f4747e528e8b899d52c)), closes [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4) [#4](https://github.com/kcmr/web-components-codemods/issues/4)
94 | * **replace-attrs:** allow to pass recast options in params ([d90ca05](https://github.com/kcmr/web-components-codemods/commit/d90ca0520a2c30393519018f81a0f21b777a749f))
95 | * **transforms:** add a codemod to replace block scope by IIFE ([db57c7b](https://github.com/kcmr/web-components-codemods/commit/db57c7b2474b585634ed546c72182af7a709dedc))
96 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Web Components Codemods
2 |
3 | [](https://travis-ci.com/kcmr/web-components-codemods)
4 | [](http://commitizen.github.io/cz-cli/)
5 | [](https://github.com/semantic-release/semantic-release)
6 | [](https://codecov.io/gh/kcmr/web-components-codemods)
7 | 
8 |
9 | [](https://nodei.co/npm/web-components-codemods/)
10 |
11 | Codemods for Web Components.
12 | Breaking changes? Don't panic :)
13 |
14 | ## Table of contents
15 |
16 | - [Usage](#usage)
17 | - [Using the included CLI](#using-the-included-cli)
18 | - [Using jscodeshift](#using-jscodeshift)
19 | - [Available codemods](#available-codemods)
20 | - [Replace attrs](#replace-attrs)
21 | - [Replace block scope by IIFE](#replace-block-scope-by-iife)
22 | - [Rename tag](#rename-tag)
23 | - [LitElement to Lit imports](#litelement-to-lit-imports)
24 | - [Acknowledgments](#acknowledgments)
25 |
26 | ## Usage
27 |
28 | The available codemods can be run in two ways: by using the included CLI or running the transform scripts directly with jscodeshift.
29 |
30 | ### Using the included CLI
31 |
32 | Install this package globally:
33 |
34 | ```bash
35 | npm i -g web-components-codemods
36 | ```
37 |
38 | Run the command in the directory you want to run a transform (the directory can be changed later):
39 |
40 | ```bash
41 | kodemod
42 | ```
43 |
44 | The command will prompt you for the transform to run and all of its options.
45 | 
46 |
47 | Alternatively, you can run a specific transform by running `kodemod `.
48 |
49 | Example:
50 |
51 | ```bash
52 | kodemod replace-attrs
53 | ```
54 |
55 | Available transform commands (same as transform scripts):
56 |
57 | - [replace-attrs](#replace-attrs)
58 | - [block-scope-to-iife](#replace-block-scope-by-iife)
59 | - [rename-tag](#rename-tag)
60 | - [lit-element-to-lit-imports](#litelement-to-lit-imports)
61 |
62 | ### Using jscodeshift
63 |
64 | Install [jscodeshift](https://github.com/facebook/jscodeshift) globally:
65 |
66 | ```bash
67 | npm i -g jscodeshift
68 | ```
69 |
70 | Clone or download this repository:
71 |
72 | ```bash
73 | npx degit kcmr/web-components-codemods
74 | ```
75 |
76 | Run `jscodeshift` passing the transform script with the `-t` option:
77 |
78 | ```bash
79 | jscodeshift target-dir/*.js -t web-components-codemods/.js
80 | ```
81 |
82 | ## Available codemods
83 |
84 | ### Replace attrs
85 |
86 | Replaces attributes in the specified tag inside a template literal tagged `html` (LitElement or lit-html).
87 |
88 | **Script**: `transforms/replace-attrs.js`
89 |
90 | **Options**
91 |
92 | | Name | Default | Type | Description |
93 | | ------------ | ----------- | --------- | -------------------------------------------------------- |
94 | | `--tag` | `undefined` | `String` | Tag name where the attributes will be replaced |
95 | | `--attrs` | `undefined` | `String` | Stringified object with `{'old-attr': 'new-attr'}` pairs |
96 | | `--tabWidth` | `4` | `Number` | Number of spaces used per tab |
97 | | `--useTabs` | `false` | `Boolean` | Use tabs instead of spaces |
98 |
99 | Example input:
100 |
101 | ```js
102 | class MyComponent extends LitElement {
103 | render() {
104 | return html`
105 |
110 |
111 | `;
112 | }
113 | }
114 | ```
115 |
116 | Command with options:
117 |
118 | ```bash
119 | jscodeshift input.js -t replace-attrs.js --tag=some-component --attrs='{"attr-one": "foo", ".someProp": ".newProp"}'
120 | ```
121 |
122 | Output:
123 |
124 | ```diff
125 | class MyComponent extends LitElement {
126 | render() {
127 | return html`
128 |
135 |
136 | `;
137 | }
138 | }
139 | ```
140 |
141 | ### Replace block scope by IIFE
142 |
143 | Replaces brackets used as scope in a file by an IIFE.
144 |
145 | **Script**: `transforms/block-scope-to-iife.js`
146 |
147 | **Options**: no options.
148 |
149 | Example input:
150 |
151 | ```js
152 | {
153 | const { Element } = Polymer;
154 | }
155 | ```
156 |
157 | Command with options:
158 |
159 | ```bash
160 | jscodeshift input.js -t block-scope-to-iife.js
161 | ```
162 |
163 | Output:
164 |
165 | ```diff
166 | -{
167 | +(function() {
168 | const { Element } = Polymer;
169 | +})();
170 | -}
171 | ```
172 |
173 | ### Rename tag
174 |
175 | Renames a tag name inside template literals and strings.
176 |
177 | **Script**: `transforms/rename-tag.js`
178 |
179 | **Options**
180 |
181 | | Name | Default | Type | Description |
182 | | ------------ | ----------- | --------- | ----------------------------- |
183 | | `--oldTag` | `undefined` | `String` | Tag name to replace |
184 | | `--newTag` | `undefined` | `String` | New tag name |
185 | | `--tabWidth` | `2` | `Number` | Number of spaces used per tab |
186 | | `--useTabs` | `false` | `Boolean` | Use tabs instead of spaces |
187 |
188 | Example input:
189 |
190 | ```js
191 | const tpl = `
192 |
193 |
194 |
195 | `;
196 | customElements.define('some-tag', SomeTag);
197 | ```
198 |
199 | Command with options:
200 |
201 | ```bash
202 | jscodeshift input.js -t rename-tag.js --oldTag=some-tag --newTag=new-tag
203 | ```
204 |
205 | Output:
206 |
207 | ```diff
208 | const tpl = `
209 | -
210 | +
211 |
212 | -
213 | +
214 | `;
215 | -customElements.define('some-tag', SomeTag);
216 | +customElements.define('new-tag', SomeTag);
217 | ```
218 |
219 | ### LitElement to Lit imports
220 |
221 | Updates the imports from `lit-element` to `lit` according to the [upgrade guide](https://lit.dev/docs/releases/upgrade/) of Lit 2.0
222 |
223 | **Script:** `transforms/lit-element-to-lit-imports.js`
224 |
225 | **Options:**
226 |
227 | | Name | Default | Type | Description |
228 | | --------- | -------- | -------- | -------------------------------- |
229 | | `--quote` | `single` | `String` | Type of quote (single or double) |
230 |
231 | Example input:
232 |
233 | ```js
234 | import { css } from 'lit-element';
235 | import { LitElement, html, property as foo, customElement } from 'lit-element';
236 | import { repeat } from 'lit-html/directives/repeat.js';
237 | import { ifDefined } from 'lit-html/directives/if-defined';
238 | ```
239 |
240 | Command with options:
241 |
242 | ```bash
243 | jscodeshift input.js -t lit-element-to-lit-imports.js
244 | ```
245 |
246 | Output:
247 |
248 | ```diff
249 | -import { css } from 'lit-element';
250 | +import { css } from 'lit';
251 | -import { LitElement, html, property as foo, customElement } from 'lit-element';
252 | +import { LitElement, html } from 'lit';
253 | +import { property as foo, customElement } from 'lit/decorators.js';
254 | -import { repeat } from 'lit-html/directives/repeat.js';
255 | +import { repeat } from 'lit/directives/repeat.js';
256 | -import { ifDefined } from 'lit-html/directives/if-defined';
257 | +import { ifDefined } from 'lit/directives/if-defined';
258 | ```
259 |
260 | ## Acknowledgments
261 |
262 | **Inspiration**
263 |
264 | - [Fearless refactors con AST - Speaker Deck](https://speakerdeck.com/sanguino/fearless-refactors-con-ast)
265 | - [React Codemod](https://github.com/reactjs/react-codemod)
266 |
267 | **Resources**
268 |
269 | - [Write Code to Rewrite Your Code: jscodeshift](https://www.toptal.com/javascript/write-code-to-rewrite-your-code)
270 | - [jscodeshift API and demos](https://hackmd.io/@yQp_d2iwRF25H5YTCeWj0w/Hy8FL6IWZ?type=view#jscodeshift-draft)
271 |
272 | ## License
273 |
274 | This project is licensed under the [MIT License](LICENSE).
275 |
--------------------------------------------------------------------------------