0

WebUI: Prep for removing useDefineForClassFields dependency.

- Add a new 389737066_migration_lit.js jscodeshift codemod that
   automatically updates Lit code as needed.
 - Update validate_tsconfig.py to restrict valid values for allowed
   flags.
 - Add tsconfig_base_lit_389737066.json and
   tsconfig_base_polymer_389737066.json configurations for Lit and
   Polymer respectively.

In this CL simply adding all the necessary pieces for subsequent CLs
to actually update Lit/Polymer code as needed.

Bug: 389737066
Change-Id: I52120556df27c95d21acff83f95e1afcdf92d2b6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6266790
Commit-Queue: Demetrios Papadopoulos <dpapad@chromium.org>
Reviewed-by: Rebekah Potter <rbpotter@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1422306}
This commit is contained in:
dpapad
2025-02-19 18:15:53 -08:00
committed by Chromium LUCI CQ
parent 247b49466f
commit 86cb3ddf99
6 changed files with 111 additions and 27 deletions

@ -0,0 +1,8 @@
{
"extends": "./tsconfig_base_lit.json",
"compilerOptions": {
"target": "ES2024",
"lib": ["ES2024", "DOM", "DOM.Iterable"],
"useDefineForClassFields": true
}
}

@ -0,0 +1,6 @@
{
"extends": "./tsconfig_base_polymer.json",
"compilerOptions": {
"useDefineForClassFields": true
}
}

@ -38,21 +38,23 @@ _allowed_config_options = [
'compilerOptions',
]
# Allowed compilerOptions
_allowed_compiler_options = [
'allowUmdGlobalAccess',
'isolatedModules',
'lib',
'noPropertyAccessFromIndexSignature',
'noUncheckedIndexedAccess',
'noUncheckedSideEffectImports',
'noUnusedLocals',
'skipLibCheck',
'strictPropertyInitialization',
'typeRoots',
'types',
'useDefineForClassFields',
]
# Allowed compilerOptions. A 'None' value indicates that all values are allowed,
# otherwise only the set of specified values is allowed.
_allowed_compiler_options = {
'allowUmdGlobalAccess': None,
'isolatedModules': None,
'lib': None,
'noPropertyAccessFromIndexSignature': None,
'noUncheckedIndexedAccess': None,
'noUncheckedSideEffectImports': None,
'noUnusedLocals': None,
'skipLibCheck': None,
'strictPropertyInitialization': None,
'target': ['ESNext', 'ES2024'],
'typeRoots': None,
'types': None,
'useDefineForClassFields': None,
}
def validateTsconfigJson(tsconfig, tsconfig_file, is_base_tsconfig):
@ -88,10 +90,16 @@ def validateTsconfigJson(tsconfig, tsconfig_file, is_base_tsconfig):
return True, None
if not is_base_tsconfig:
for input_param in tsconfig['compilerOptions'].keys():
if input_param not in _allowed_compiler_options:
return False, f'Disallowed |{input_param}| flag detected in '+ \
for param, param_value in tsconfig['compilerOptions'].items():
if param not in _allowed_compiler_options:
return False, f'Disallowed |{param}| flag detected in '+ \
f'{tsconfig_file}.'
else:
allowed_values = _allowed_compiler_options[param]
if (allowed_values is not None and param_value not in allowed_values):
return False, f'Disallowed value |{param_value}| for |{param}| ' + \
f'flag detected in {tsconfig_file}. Must be one of ' + \
f'{allowed_values}.'
return True, None

@ -0,0 +1,53 @@
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Codemod for adding a "accessor" keyword before every Lit property
// declaration in a class. To be used to update Lit UIs for the
// purposes of fixing cbug.com/389737066.
module.exports = function transformer(file, api) {
const source = file.source;
const j = api.jscodeshift;
const root = j(source);
const classProperties = new Set();
root.find(j.Function, {key: {name: 'properties'}})
.find(j.ObjectExpression)
.forEach(p => {
p.value.properties.forEach(property => {
// Ignore nested ObjectExpressions.
if (p.parentPath.value.type !== 'ReturnStatement') {
return;
}
classProperties.add(property.key.name);
});
});
root.find(j.ClassDeclaration).forEach(path => {
path.node.body.body.forEach(classMember => {
if (classMember.type === 'ClassProperty' &&
classProperties.has(classMember.key.name)) {
// Add an @accessor decorator since jscodeshift does not support the
// "accessor" keyword yet. It will be replaced later using a regular
// expression.
const decorator = j.decorator(j.identifier('accessor'));
classMember.decorators = [decorator];
}
});
});
const outputOptions = {quote: 'single'};
let out = root.toSource(outputOptions);
// Replace @accessor with the 'accessor' keyword and place it on the same line
// as the property.
out = out.replaceAll(/@accessor\n\s+/g, 'accessor ');
// Fix error: "error TS1029: 'private' modifier must precede 'accessor'
// modifier."
out = out.replaceAll(/\baccessor private\b/g, 'private accessor');
out = out.replaceAll(/\baccessor protected\b/g, 'protected accessor');
return out;
};

@ -11,7 +11,7 @@ module.exports = function transformer(file, api) {
const j = api.jscodeshift;
const root = j(source);
const polymerProperties = new Set();
const classProperties = new Set();
root.find(j.Function, {key: {name: 'properties'}})
.find(j.ObjectExpression)
.forEach(p => {
@ -20,14 +20,14 @@ module.exports = function transformer(file, api) {
if (p.parentPath.value.type !== 'ReturnStatement') {
return;
}
polymerProperties.add(property.key.name);
classProperties.add(property.key.name);
});
});
root.find(j.ClassDeclaration).forEach(path => {
path.node.body.body.forEach(classMember => {
if (classMember.type === 'ClassProperty' &&
polymerProperties.has(classMember.key.name)) {
classProperties.has(classMember.key.name)) {
classMember.declare = true;
}
});

@ -7,7 +7,7 @@ import argparse
import os
import sys
"""
Helper script for updating Polymer code to address https://crbug.com/389737066.
Helper script for running jscodeshift codemods over a set of files.
"""
_HERE_PATH = os.path.dirname(__file__)
@ -30,25 +30,34 @@ import node
3) Invoke the script from the root directory of the repository. For example
python3 ui/webui/resources/tools/codemods/389737066_migration.py \
--files chrome/browser/resources/print_preview/ui/color_settings.js
python3 ui/webui/resources/tools/codemods/jscodeshift.py \
--transform ui/webui/resources/tools/codemods/my_transform.js
--files ui/webui/resources/cr_elements/cr_button/cr_button.ts
python3 ui/webui/resources/tools/codemods/389737066_migration.py \
--files `find chrome/browser/resources/print_preview/ui/ -name '*.ts'`
python3 ui/webui/resources/tools/codemods/jscodeshift.py \
--transform ui/webui/resources/tools/codemods/my_transform.js
--files `find ui/webui/resources/cr_elements/cr_button/ -name '*.ts'`
"""
def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument('--transform', required=True)
parser.add_argument('--files', nargs='*', required=True)
args = parser.parse_args(argv)
if not os.path.exists(args.transform):
print(
f'Error: jscodeshift.py: Could not file transform file \'args.transform\'',
file=sys.stderr)
sys.exit(1)
print(f'Migrating {len(args.files)} files...')
# Update TS file.
out = node.RunNode([
os.path.join(_HERE_PATH, 'node_modules/jscodeshift/bin/jscodeshift.js'),
'--transform=' + os.path.join(_HERE_PATH, '389737066_migration.js'),
'--transform=' + args.transform,
'--extensions=ts',
'--parser=ts',
] + args.files)