├── .github ├── dependabot.yml └── workflows │ └── test.yml ├── .gitignore ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md ├── bin └── smoke-test.sh ├── jest.config.js ├── package.json ├── tests ├── fixtures.spec.js ├── fixtures │ ├── html-template-cleanup │ │ ├── closing-of-element-with-open-child-elements │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── end-tag-without-matching-open-element │ │ │ ├── basic │ │ │ │ ├── expected │ │ │ │ │ ├── logs.json │ │ │ │ │ └── x │ │ │ │ │ │ └── component │ │ │ │ │ │ ├── component.html │ │ │ │ │ │ └── component.js │ │ │ │ └── input │ │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ └── multiple │ │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── eof-in-element-that-can-contain-only-text │ │ │ └── basic │ │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── invalid-template-attributes │ │ │ ├── basic │ │ │ │ ├── expected │ │ │ │ │ ├── logs.json │ │ │ │ │ └── x │ │ │ │ │ │ └── component │ │ │ │ │ │ ├── component.html │ │ │ │ │ │ └── component.js │ │ │ │ └── input │ │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ └── complex │ │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ └── multiple-template-if-true-if-false │ │ │ ├── basic │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ │ └── complex │ │ │ ├── expected │ │ │ ├── logs.json │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ │ └── input │ │ │ └── x │ │ │ └── component │ │ │ ├── component.html │ │ │ └── component.js │ ├── shadow-to-light │ │ ├── already-light-with-scoped-css │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ ├── component.js │ │ │ │ │ └── component.scoped.css │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ ├── component.js │ │ │ │ └── component.scoped.css │ │ ├── already-light │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── basic-css-and-scoped-css-with-dom-manual │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.css │ │ │ │ │ ├── component.html │ │ │ │ │ ├── component.js │ │ │ │ │ └── component.scoped.css │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.css │ │ │ │ ├── component.html │ │ │ │ ├── component.js │ │ │ │ └── component.scoped.css │ │ ├── basic-css-and-scoped-css │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ ├── component.js │ │ │ │ │ └── component.scoped.css │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.css │ │ │ │ ├── component.html │ │ │ │ ├── component.js │ │ │ │ └── component.scoped.css │ │ ├── basic-css-with-dom-manual │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.css │ │ │ │ │ ├── component.html │ │ │ │ │ ├── component.js │ │ │ │ │ └── component.scoped.css │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.css │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── basic-css │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ ├── component.js │ │ │ │ │ └── component.scoped.css │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.css │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── basic-scss │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ ├── component.js │ │ │ │ │ └── component.scoped.scss │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ ├── component.js │ │ │ │ └── component.scss │ │ ├── basic-slot-no-wrapper-needed │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ ├── component.js │ │ │ │ │ └── component.scoped.css │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.css │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── basic-slot-with-defaults │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ ├── component.js │ │ │ │ │ └── component.scoped.css │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.css │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── basic-slot │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ ├── component.js │ │ │ │ │ └── component.scoped.css │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.css │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── basic-typescript │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.ts │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.ts │ │ ├── basic │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── css-imported-from-js-and-html │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ ├── component.js │ │ │ │ │ └── component.scoped.css │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.css │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── css-imported-from-js-and-not-html │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ ├── component.js │ │ │ │ │ └── yolo.scoped.css │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ ├── component.js │ │ │ │ └── yolo.css │ │ ├── css-imported-from-js-twice │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.js │ │ │ │ │ └── yolo.scoped.css │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.js │ │ │ │ └── yolo.css │ │ ├── css-imported-from-js │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ ├── component.js │ │ │ │ │ └── component.scoped.css │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.css │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── css-with-dom-manual-combinators │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.css │ │ │ │ │ ├── component.html │ │ │ │ │ ├── component.js │ │ │ │ │ └── component.scoped.css │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.css │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── dom-manual │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── explicitly-shadow │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── invalid-js │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ ├── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ │ │ └── invalid │ │ │ │ │ └── invalid.js │ │ │ └── input │ │ │ │ └── x │ │ │ │ ├── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ │ │ └── invalid │ │ │ │ └── invalid.js │ │ ├── multi-template │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── a.html │ │ │ │ │ ├── b.html │ │ │ │ │ └── component.js │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── a.html │ │ │ │ ├── b.html │ │ │ │ └── component.js │ │ ├── scoped-css-imported-from-js │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.js │ │ │ │ │ └── yolo.scoped.css │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.js │ │ │ │ └── yolo.scoped.css │ │ ├── this-dot-template-destructuring-deep │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── this-dot-template-destructuring-rename │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ ├── this-dot-template-destructuring │ │ │ ├── expected │ │ │ │ ├── logs.json │ │ │ │ └── x │ │ │ │ │ └── component │ │ │ │ │ ├── component.html │ │ │ │ │ └── component.js │ │ │ └── input │ │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ └── this-dot-template │ │ │ ├── expected │ │ │ ├── logs.json │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ │ └── input │ │ │ └── x │ │ │ └── component │ │ │ ├── component.html │ │ │ └── component.js │ └── synthetic-to-native │ │ ├── no-prop │ │ ├── expected │ │ │ ├── logs.json │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ └── input │ │ │ └── x │ │ │ └── component │ │ │ ├── component.html │ │ │ └── component.js │ │ ├── prop-already-correct │ │ ├── expected │ │ │ ├── logs.json │ │ │ └── x │ │ │ │ └── component │ │ │ │ ├── component.html │ │ │ │ └── component.js │ │ └── input │ │ │ └── x │ │ │ └── component │ │ │ ├── component.html │ │ │ └── component.js │ │ └── prop-already-exists │ │ ├── expected │ │ ├── logs.json │ │ └── x │ │ │ └── component │ │ │ ├── component.html │ │ │ └── component.js │ │ └── input │ │ └── x │ │ └── component │ │ ├── component.html │ │ └── component.js └── utils │ └── jestUtils.js ├── transforms ├── cli.js ├── fsUtils.js ├── htmlTemplateCleanup │ ├── closingOfElementWithOpenChildElements.js │ ├── endTagWithoutMatchingOpenElement.js │ ├── eofInElementThatCanContainOnlyText.js │ ├── index.js │ ├── invalidTemplateAttributes.js │ └── multipleIfTrueIfFalseAttributes.js ├── index.js ├── jsUtils.js ├── jscodeshift.js ├── observer.js ├── parse5.js ├── parse5Utils.js ├── postcssSelectorParser.js ├── runTransform.js ├── setupCliLogger.js ├── shadowToLight.js ├── shadowToLight │ ├── index.js │ ├── modifyComponentJavaScript.js │ ├── modifyTemplateCss.js │ └── modifyTemplateHtml.js ├── syntheticToNative │ └── index.js └── walkComponents.js └── yarn.lock /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "npm" # See documentation for possible values 7 | directory: "/" # Location of package manifests 8 | schedule: 9 | interval: "monthly" 10 | groups: 11 | # Non-major version bumps hopefully shouldn't break anything, 12 | # so let's group them together into a single PR! 13 | theoretically-non-breaking: 14 | update-types: 15 | - "minor" 16 | - "patch" 17 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches: [ main ] 5 | pull_request: 6 | branches: [ main ] 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | node-version: [ '18.0.0', '20' ] 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Use Node.js ${{ matrix.node-version }} 16 | uses: actions/setup-node@v3 17 | with: 18 | node-version: ${{ matrix.node-version }} 19 | cache: 'yarn' 20 | - run: yarn --frozen-lockfile 21 | - run: yarn lint 22 | - run: yarn test 23 | - run: bash ./bin/smoke-test.sh 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Comment line immediately above ownership line is reserved for related gus information. Please be careful while editing. 2 | #ECCN:Open Source -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Salesforce Open Source Community Code of Conduct 2 | 3 | ## About the Code of Conduct 4 | 5 | Equality is a core value at Salesforce. We believe a diverse and inclusive 6 | community fosters innovation and creativity, and are committed to building a 7 | culture where everyone feels included. 8 | 9 | Salesforce open-source projects are committed to providing a friendly, safe, and 10 | welcoming environment for all, regardless of gender identity and expression, 11 | sexual orientation, disability, physical appearance, body size, ethnicity, nationality, 12 | race, age, religion, level of experience, education, socioeconomic status, or 13 | other similar personal characteristics. 14 | 15 | The goal of this code of conduct is to specify a baseline standard of behavior so 16 | that people with different social values and communication styles can work 17 | together effectively, productively, and respectfully in our open source community. 18 | It also establishes a mechanism for reporting issues and resolving conflicts. 19 | 20 | All questions and reports of abusive, harassing, or otherwise unacceptable behavior 21 | in a Salesforce open-source project may be reported by contacting the Salesforce 22 | Open Source Conduct Committee at ossconduct@salesforce.com. 23 | 24 | ## Our Pledge 25 | 26 | In the interest of fostering an open and welcoming environment, we as 27 | contributors and maintainers pledge to making participation in our project and 28 | our community a harassment-free experience for everyone, regardless of gender 29 | identity and expression, sexual orientation, disability, physical appearance, 30 | body size, ethnicity, nationality, race, age, religion, level of experience, education, 31 | socioeconomic status, or other similar personal characteristics. 32 | 33 | ## Our Standards 34 | 35 | Examples of behavior that contributes to creating a positive environment 36 | include: 37 | 38 | * Using welcoming and inclusive language 39 | * Being respectful of differing viewpoints and experiences 40 | * Gracefully accepting constructive criticism 41 | * Focusing on what is best for the community 42 | * Showing empathy toward other community members 43 | 44 | Examples of unacceptable behavior by participants include: 45 | 46 | * The use of sexualized language or imagery and unwelcome sexual attention or 47 | advances 48 | * Personal attacks, insulting/derogatory comments, or trolling 49 | * Public or private harassment 50 | * Publishing, or threatening to publish, others' private information—such as 51 | a physical or electronic address—without explicit permission 52 | * Other conduct which could reasonably be considered inappropriate in a 53 | professional setting 54 | * Advocating for or encouraging any of the above behaviors 55 | 56 | ## Our Responsibilities 57 | 58 | Project maintainers are responsible for clarifying the standards of acceptable 59 | behavior and are expected to take appropriate and fair corrective action in 60 | response to any instances of unacceptable behavior. 61 | 62 | Project maintainers have the right and responsibility to remove, edit, or 63 | reject comments, commits, code, wiki edits, issues, and other contributions 64 | that are not aligned with this Code of Conduct, or to ban temporarily or 65 | permanently any contributor for other behaviors that they deem inappropriate, 66 | threatening, offensive, or harmful. 67 | 68 | ## Scope 69 | 70 | This Code of Conduct applies both within project spaces and in public spaces 71 | when an individual is representing the project or its community. Examples of 72 | representing a project or community include using an official project email 73 | address, posting via an official social media account, or acting as an appointed 74 | representative at an online or offline event. Representation of a project may be 75 | further defined and clarified by project maintainers. 76 | 77 | ## Enforcement 78 | 79 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 80 | reported by contacting the Salesforce Open Source Conduct Committee 81 | at ossconduct@salesforce.com. All complaints will be reviewed and investigated 82 | and will result in a response that is deemed necessary and appropriate to the 83 | circumstances. The committee is obligated to maintain confidentiality with 84 | regard to the reporter of an incident. Further details of specific enforcement 85 | policies may be posted separately. 86 | 87 | Project maintainers who do not follow or enforce the Code of Conduct in good 88 | faith may face temporary or permanent repercussions as determined by other 89 | members of the project's leadership and the Salesforce Open Source Conduct 90 | Committee. 91 | 92 | ## Attribution 93 | 94 | This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant-home], 95 | version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html. 96 | It includes adaptions and additions from [Go Community Code of Conduct][golang-coc], 97 | [CNCF Code of Conduct][cncf-coc], and [Microsoft Open Source Code of Conduct][microsoft-coc]. 98 | 99 | This Code of Conduct is licensed under the [Creative Commons Attribution 3.0 License][cc-by-3-us]. 100 | 101 | [contributor-covenant-home]: https://www.contributor-covenant.org (https://www.contributor-covenant.org/) 102 | [golang-coc]: https://golang.org/conduct 103 | [cncf-coc]: https://github.com/cncf/foundation/blob/master/code-of-conduct.md 104 | [microsoft-coc]: https://opensource.microsoft.com/codeofconduct/ 105 | [cc-by-3-us]: https://creativecommons.org/licenses/by/3.0/us/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022, Salesforce.com, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | * Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lwc-codemod 2 | 3 | Codemods for [LWC](https://lwc.dev/). In other words: scripts to transform LWC component code. 4 | 5 | This tool currently can: 6 | 7 | - convert components from shadow DOM to [light DOM](https://lwc.dev/guide/light_dom#light-dom-(developer-preview)) 8 | - convert components from synthetic shadow DOM to [native shadow DOM](https://rfcs.lwc.dev/rfcs/lwc/0115-mixed-shadow-mode) 9 | - clean up HTML syntax errors 10 | 11 | ## Installation 12 | 13 | ```sh 14 | npm i -g lwc-codemod 15 | ``` 16 | 17 | Or, to avoid installing globally, use `npx lwc-codemod`. 18 | 19 | ## Basic usage 20 | 21 | ```sh 22 | lwc-codemod 23 | ``` 24 | 25 | When you pass in a ``, the script will crawl all components inside of that path: 26 | 27 | ```sh 28 | lwc-codemod /path/to/my/components/ 29 | ``` 30 | 31 | ## Transforms 32 | 33 | Available transforms: 34 | 35 | - [Shadow DOM to Light DOM](#shadow-dom-to-light-dom) (`shadow-to-light`) 36 | - [Synthetic Shadow DOM to Native Shadow DOM](#synthetic-shadow-dom-to-native-shadow-dom) (`synthetic-to-native`) 37 | - [HTML Template Cleanup](#html-template-cleanup) (`html-template-cleanup`) 38 | 39 | ### Shadow DOM to Light DOM 40 | 41 | #### Usage 42 | 43 | ```sh 44 | lwc-codemod shadow-to-light 45 | ``` 46 | 47 | #### Summary 48 | 49 | Converts components from shadow DOM to light DOM. 50 | 51 | #### JS 52 | 53 | - Adds `static renderMode = 'light'` 54 | - `this.template` -> `this` (and destructuring equivalents) 55 | 56 | #### HTML 57 | 58 | - Adds `lwc:render-mode="light"` 59 | - Removes `lwc:dom="manual"` 60 | 61 | #### CSS 62 | 63 | - Moves `foo.css` to `foo.scoped.css` 64 | 65 | #### `lwc:dom="manual"` 66 | 67 | For this case, the styles still need to be scoped to the `lwc:dom="manual"` tree. So the codemod creates an additional global CSS file containing selectors to replace e.g. `div` with `.auto-generated div`, where `auto-generated` is a class applied to `lwc:dom="manual"` nodes. 68 | 69 | #### Slots 70 | 71 | For slots, a wrapper `
` is created around the ``so that this `
` can be targeted in the CSS and have events attached to it, e.g. `onscroll` events. 72 | 73 | `onslotchange` is currently not implemented. As an alternative, you can use [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) or explicit signalling between components to notify of changes. 74 | 75 | ### Synthetic Shadow DOM to Native Shadow DOM 76 | 77 | #### Usage 78 | 79 | ```sh 80 | lwc-codemod synthetic-to-native 81 | ``` 82 | 83 | #### Summary 84 | 85 | Converts components from synthetic shadow to native shadow. 86 | 87 | The only transformation it currently applies is to add the `static shadowSupportMode` property. Any other discrepancies between native shadow and synthetic shadow will have to be handled manually. 88 | 89 | ### HTML Template Cleanup 90 | 91 | #### Usage 92 | 93 | ```sh 94 | lwc-codemod html-template-cleanup 95 | ``` 96 | 97 | #### Summary 98 | 99 | Fixes several HTML warnings/errors generated by the `@lwc/template-compiler` 100 | 101 | ##### Multiple `if:true`/`if:false` Directives on the Same Element 102 | 103 | Removes excessive `if:true` and `if:false` attributes that appear on the same element. 104 | 105 | Only the first `if:true`/`if:false` is processed in LWC. This mod will remove all other instances, since they would have no effect. 106 | 107 | For example: 108 | 109 | ```html 110 | 113 | ``` 114 | 115 | Will become 116 | 117 | ```html 118 | 121 | ``` 122 | 123 | ##### Invalid Template Attributes 124 | 125 | Removes invalid attributes from both root and non-root `templates`. 126 | 127 | These attributes are ignored by LWC during parsing, and are thus safe to remove. 128 | 129 | For example: 130 | 131 | ```html 132 | 135 | ``` 136 | 137 | Will become: 138 | 139 | ```html 140 | 143 | ``` 144 | 145 | See the official [documentation](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.reference_directives) for valid `template` attributes. 146 | 147 | Note that for non-root `template` elements, if there aren't any valid directives, the content inside the `template` will not be rendered. 148 | 149 | As a corrective measure, this mod will therefore remove any `template` elements without valid directives. 150 | 151 | ##### HTML parsing errors 152 | 153 | Fixes errors generated during LWC HTML parsing. 154 | 155 | The following transforms are available for each errors: 156 | 157 | ###### `eof-in-element-that-can-contain-only-text` 158 | 159 | This is an unexpected end of file in an element that can only contain text. 160 | 161 | ```html 162 | -------------------------------------------------------------------------------- /tests/fixtures/html-template-cleanup/eof-in-element-that-can-contain-only-text/basic/expected/x/component/component.js: -------------------------------------------------------------------------------- 1 | import { LightningElement } from 'lwc' 2 | 3 | export default class extends LightningElement { 4 | } 5 | -------------------------------------------------------------------------------- /tests/fixtures/html-template-cleanup/eof-in-element-that-can-contain-only-text/basic/input/x/component/component.html: -------------------------------------------------------------------------------- 1 |