diff --git a/src/m365/spfx/commands/project/project-doctor.spec.ts b/src/m365/spfx/commands/project/project-doctor.spec.ts index b2688694621..be31eef16fa 100644 --- a/src/m365/spfx/commands/project/project-doctor.spec.ts +++ b/src/m365/spfx/commands/project/project-doctor.spec.ts @@ -594,7 +594,7 @@ describe(commands.PROJECT_DOCTOR, () => { }); it('e2e: shows correct number of findings for a valid 1.22.0-beta.1 project', async () => { - sinon.stub(command as any, 'getProjectRoot').callsFake(_ => path.join(process.cwd(), 'src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react')); + sinon.stub(command as any, 'getProjectRoot').callsFake(_ => path.join(process.cwd(), 'src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react')); await command.action(logger, { options: {} } as any); const findings: FindingToReport[] = log[0]; diff --git a/src/m365/spfx/commands/project/project-doctor.ts b/src/m365/spfx/commands/project/project-doctor.ts index c130ee88c73..8906f0db9e0 100644 --- a/src/m365/spfx/commands/project/project-doctor.ts +++ b/src/m365/spfx/commands/project/project-doctor.ts @@ -78,7 +78,7 @@ class SpfxProjectDoctorCommand extends BaseProjectCommand { '1.20.0', '1.21.0', '1.21.1', - '1.22.0-beta.1' + '1.22.0-rc.0' ]; protected get allowedOutputs(): string[] { diff --git a/src/m365/spfx/commands/project/project-doctor/doctor-1.22.0-beta.1.ts b/src/m365/spfx/commands/project/project-doctor/doctor-1.22.0-rc.0.ts similarity index 92% rename from src/m365/spfx/commands/project/project-doctor/doctor-1.22.0-beta.1.ts rename to src/m365/spfx/commands/project/project-doctor/doctor-1.22.0-rc.0.ts index f1f04b8f213..aeb9aa9b5fa 100644 --- a/src/m365/spfx/commands/project/project-doctor/doctor-1.22.0-beta.1.ts +++ b/src/m365/spfx/commands/project/project-doctor/doctor-1.22.0-rc.0.ts @@ -15,7 +15,7 @@ export default [ new FN002013_DEVDEP_types_webpack_env('~1.15.2'), new FN002015_DEVDEP_types_react('17'), new FN002016_DEVDEP_types_react_dom('17'), - new FN002021_DEVDEP_rushstack_eslint_config('4.3.0'), - new FN002022_DEVDEP_typescript('5.3.3'), + new FN002021_DEVDEP_rushstack_eslint_config('4.5.2'), + new FN002022_DEVDEP_typescript('~5.8.0'), new FN021001_PKG_spfx_deps_versions_match_project_version(true) ]; \ No newline at end of file diff --git a/src/m365/spfx/commands/project/project-model/PackageJson.ts b/src/m365/spfx/commands/project/project-model/PackageJson.ts index 1de1340573c..e4af34675c7 100644 --- a/src/m365/spfx/commands/project/project-model/PackageJson.ts +++ b/src/m365/spfx/commands/project/project-model/PackageJson.ts @@ -18,5 +18,8 @@ export interface PackageJson extends JsonFile { 'package-solution'?: string; start?: string; test?: string; + 'test-only'?: string; + 'trust-dev-cert'?: string; + 'untrust-dev-cert'?: string; } } \ No newline at end of file diff --git a/src/m365/spfx/commands/project/project-upgrade.spec.ts b/src/m365/spfx/commands/project/project-upgrade.spec.ts index a02ce33345c..8982964f6b7 100644 --- a/src/m365/spfx/commands/project/project-upgrade.spec.ts +++ b/src/m365/spfx/commands/project/project-upgrade.spec.ts @@ -3506,69 +3506,69 @@ describe(commands.PROJECT_UPGRADE, () => { }); //#endregion - //#region 1.22.1 - it('e2e: shows correct number of findings for upgrading ace 1.21.1 project to 1.22.0-beta.1', async () => { + //#region 1.21.1 + it('e2e: shows correct number of findings for upgrading ace 1.21.1 project to 1.22.0-rc.0', async () => { sinon.stub(command as any, 'getProjectRoot').callsFake(_ => path.join(process.cwd(), 'src/m365/spfx/commands/project/test-projects/spfx-1211-ace')); - await command.action(logger, { options: { toVersion: '1.22.0-beta.1', preview: true, output: 'json' } } as any); + await command.action(logger, { options: { toVersion: '1.22.0-rc.0', preview: true, output: 'json' } } as any); const findings: FindingToReport[] = log[0]; - assert.strictEqual(findings.length, 42); + assert.strictEqual(findings.length, 50); }); - it('e2e: shows correct number of findings for upgrading application customizer 1.21.1 project to 1.22.0-beta.1', async () => { + it('e2e: shows correct number of findings for upgrading application customizer 1.21.1 project to 1.22.0-rc.0', async () => { sinon.stub(command as any, 'getProjectRoot').callsFake(_ => path.join(process.cwd(), 'src/m365/spfx/commands/project/test-projects/spfx-1211-applicationcustomizer')); - await command.action(logger, { options: { toVersion: '1.22.0-beta.1', preview: true, output: 'json' } } as any); + await command.action(logger, { options: { toVersion: '1.22.0-rc.0', preview: true, output: 'json' } } as any); const findings: FindingToReport[] = log[0]; - assert.strictEqual(findings.length, 44); + assert.strictEqual(findings.length, 52); }); - it('e2e: shows correct number of findings for upgrading field customizer react 1.21.1 project to 1.22.0-beta.1', async () => { + it('e2e: shows correct number of findings for upgrading field customizer react 1.21.1 project to 1.22.0-rc.0', async () => { sinon.stub(command as any, 'getProjectRoot').callsFake(_ => path.join(process.cwd(), 'src/m365/spfx/commands/project/test-projects/spfx-1211-fieldcustomizer-react')); - await command.action(logger, { options: { toVersion: '1.22.0-beta.1', preview: true, output: 'json' } } as any); + await command.action(logger, { options: { toVersion: '1.22.0-rc.0', preview: true, output: 'json' } } as any); const findings: FindingToReport[] = log[0]; - assert.strictEqual(findings.length, 44); + assert.strictEqual(findings.length, 52); }); - it('e2e: shows correct number of findings for upgrading form customizer react 1.21.1 project to 1.22.0-beta.1', async () => { + it('e2e: shows correct number of findings for upgrading form customizer react 1.21.1 project to 1.22.0-rc.0', async () => { sinon.stub(command as any, 'getProjectRoot').callsFake(_ => path.join(process.cwd(), 'src/m365/spfx/commands/project/test-projects/spfx-1211-formcustomizer-react')); - await command.action(logger, { options: { toVersion: '1.22.0-beta.1', preview: true, output: 'json' } } as any); + await command.action(logger, { options: { toVersion: '1.22.0-rc.0', preview: true, output: 'json' } } as any); const findings: FindingToReport[] = log[0]; - assert.strictEqual(findings.length, 46); + assert.strictEqual(findings.length, 54); }); - it('e2e: shows correct number of findings for upgrading list view command set 1.21.1 project to 1.22.0-beta.1', async () => { + it('e2e: shows correct number of findings for upgrading list view command set 1.21.1 project to 1.22.0-rc.0', async () => { sinon.stub(command as any, 'getProjectRoot').callsFake(_ => path.join(process.cwd(), 'src/m365/spfx/commands/project/test-projects/spfx-1211-listviewcommandset')); - await command.action(logger, { options: { toVersion: '1.22.0-beta.1', preview: true, output: 'json' } } as any); + await command.action(logger, { options: { toVersion: '1.22.0-rc.0', preview: true, output: 'json' } } as any); const findings: FindingToReport[] = log[0]; - assert.strictEqual(findings.length, 44); + assert.strictEqual(findings.length, 52); }); - it('e2e: shows correct number of findings for upgrading no framework web part 1.21.1 project to 1.22.0-beta.1', async () => { + it('e2e: shows correct number of findings for upgrading no framework web part 1.21.1 project to 1.22.0-rc.0', async () => { sinon.stub(command as any, 'getProjectRoot').callsFake(_ => path.join(process.cwd(), 'src/m365/spfx/commands/project/test-projects/spfx-1211-webpart-nolib')); - await command.action(logger, { options: { toVersion: '1.22.0-beta.1', preview: true, output: 'json' } } as any); + await command.action(logger, { options: { toVersion: '1.22.0-rc.0', preview: true, output: 'json' } } as any); const findings: FindingToReport[] = log[0]; - assert.strictEqual(findings.length, 46); + assert.strictEqual(findings.length, 54); }); - it('e2e: shows correct number of findings for upgrading react web part 1.21.1 project to 1.22.0-beta.1', async () => { + it('e2e: shows correct number of findings for upgrading react web part 1.21.1 project to 1.22.0-rc.0', async () => { sinon.stub(command as any, 'getProjectRoot').callsFake(_ => path.join(process.cwd(), 'src/m365/spfx/commands/project/test-projects/spfx-1211-webpart-react')); - await command.action(logger, { options: { toVersion: '1.22.0-beta.1', preview: true, output: 'json' } } as any); + await command.action(logger, { options: { toVersion: '1.22.0-rc.0', preview: true, output: 'json' } } as any); const findings: FindingToReport[] = log[0]; - assert.strictEqual(findings.length, 47); + assert.strictEqual(findings.length, 55); }); - it('e2e: shows correct number of findings for upgrading web part with optional dependencies 1.21.1 project to 1.22.0-beta.1', async () => { + it('e2e: shows correct number of findings for upgrading web part with optional dependencies 1.21.1 project to 1.22.0-rc.0', async () => { sinon.stub(command as any, 'getProjectRoot').callsFake(_ => path.join(process.cwd(), 'src/m365/spfx/commands/project/test-projects/spfx-1211-webpart-optionaldeps')); - await command.action(logger, { options: { toVersion: '1.22.0-beta.1', preview: true, output: 'json' } } as any); + await command.action(logger, { options: { toVersion: '1.22.0-rc.0', preview: true, output: 'json' } } as any); const findings: FindingToReport[] = log[0]; - assert.strictEqual(findings.length, 55); + assert.strictEqual(findings.length, 63); }); //#endregion diff --git a/src/m365/spfx/commands/project/project-upgrade.ts b/src/m365/spfx/commands/project/project-upgrade.ts index 18387a09979..b61527f12a8 100644 --- a/src/m365/spfx/commands/project/project-upgrade.ts +++ b/src/m365/spfx/commands/project/project-upgrade.ts @@ -87,7 +87,7 @@ class SpfxProjectUpgradeCommand extends BaseProjectCommand { '1.20.0', '1.21.0', '1.21.1', - '1.22.0-beta.1' + '1.22.0-rc.0' ]; public static ERROR_NO_PROJECT_ROOT_FOLDER: number = 1; diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN002033_DEVDEP_jest_junit.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN002033_DEVDEP_css_loader.ts similarity index 57% rename from src/m365/spfx/commands/project/project-upgrade/rules/FN002033_DEVDEP_jest_junit.ts rename to src/m365/spfx/commands/project/project-upgrade/rules/FN002033_DEVDEP_css_loader.ts index bc36be799da..e1a0b2b8316 100644 --- a/src/m365/spfx/commands/project/project-upgrade/rules/FN002033_DEVDEP_jest_junit.ts +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN002033_DEVDEP_css_loader.ts @@ -1,8 +1,8 @@ import { DependencyRule } from "./DependencyRule.js"; -export class FN002033_DEVDEP_jest_junit extends DependencyRule { +export class FN002033_DEVDEP_css_loader extends DependencyRule { constructor(packageVersion: string) { - super('jest-junit', packageVersion, true); + super('css-loader', packageVersion, true); } get id(): string { diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN002034_DEVDEP_microsoft_spfx_heft_plugins.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN002034_DEVDEP_microsoft_spfx_heft_plugins.ts new file mode 100644 index 00000000000..d38c324c16b --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN002034_DEVDEP_microsoft_spfx_heft_plugins.ts @@ -0,0 +1,11 @@ +import { DependencyRule } from "./DependencyRule.js"; + +export class FN002034_DEVDEP_microsoft_spfx_heft_plugins extends DependencyRule { + constructor(packageVersion: string) { + super('@microsoft/spfx-heft-plugins', packageVersion, true); + } + + get id(): string { + return 'FN002034'; + } +} \ No newline at end of file diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN002035_DEVDEP_types_heft_jest.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN002035_DEVDEP_types_heft_jest.ts new file mode 100644 index 00000000000..82e8847d09e --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN002035_DEVDEP_types_heft_jest.ts @@ -0,0 +1,11 @@ +import { DependencyRule } from "./DependencyRule.js"; + +export class FN002035_DEVDEP_types_heft_jest extends DependencyRule { + constructor(packageVersion: string) { + super('@types/heft-jest', packageVersion, true); + } + + get id(): string { + return 'FN002035'; + } +} \ No newline at end of file diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN015012_FILE_config_heft_json.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN015012_FILE_config_heft_json.ts deleted file mode 100644 index 1841867ce81..00000000000 --- a/src/m365/spfx/commands/project/project-upgrade/rules/FN015012_FILE_config_heft_json.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { FileAddRemoveRule } from "./FileAddRemoveRule.js"; - -export class FN015012_FILE_config_heft_json extends FileAddRemoveRule { - constructor(add: boolean, contents: string | undefined) { - super('./config/heft.json', add, contents); - } - - get id(): string { - return 'FN015012'; - } -} diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN015013_FILE_config_jest_config_json.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN015013_FILE_config_jest_config_json.ts deleted file mode 100644 index 3502abb3d5e..00000000000 --- a/src/m365/spfx/commands/project/project-upgrade/rules/FN015013_FILE_config_jest_config_json.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { FileAddRemoveRule } from "./FileAddRemoveRule.js"; - -export class FN015013_FILE_config_jest_config_json extends FileAddRemoveRule { - constructor(add: boolean, contents: string | undefined) { - super('./config/jest.config.json', add, contents); - } - - get id(): string { - return 'FN015013'; - } -} diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN021014_PKG_scripts_test_only.spec.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN021014_PKG_scripts_test_only.spec.ts new file mode 100644 index 00000000000..cd1df7ab0a5 --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN021014_PKG_scripts_test_only.spec.ts @@ -0,0 +1,73 @@ +import assert from 'assert'; +import { Project } from '../../project-model/index.js'; +import { Finding } from '../../report-model/index.js'; +import { FN021014_PKG_scripts_test_only } from './FN021014_PKG_scripts_test_only.js'; + +describe('FN021014_PKG_scripts_test_only', () => { + let findings: Finding[]; + let rule: FN021014_PKG_scripts_test_only; + + beforeEach(() => { + findings = []; + rule = new FN021014_PKG_scripts_test_only('heft run --only test --'); + }); + + it(`doesn't return notification if package.json is not available`, () => { + const project: Project = { + path: '/usr/tmp' + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it(`returns notification if scripts property is not defined`, () => { + const project: Project = { + path: '/usr/tmp', + packageJson: {} + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 1); + }); + + it(`returns notification if scripts.test-only property is not defined`, () => { + const project: Project = { + path: '/usr/tmp', + packageJson: { + scripts: {} + } + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 1); + }); + + it(`returns notification if scripts.test-only property is different than expected`, () => { + const project: Project = { + path: '/usr/tmp', + packageJson: { + scripts: { + 'test-only': 'test-only' + } + } + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 1); + }); + + it(`returns correct node when scripts.test-only is set to a string`, () => { + const project: Project = { + path: '/usr/tmp', + packageJson: { + scripts: { + 'test-only': 'test-only' + }, + source: JSON.stringify({ + scripts: { + 'test-only': 'test-only' + } + }, null, 2) + } + }; + rule.visit(project, findings); + assert.strictEqual(findings[0].occurrences[0].position?.line, 3); + }); +}); diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN021014_PKG_scripts_test_only.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN021014_PKG_scripts_test_only.ts new file mode 100644 index 00000000000..dcb50e3c889 --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN021014_PKG_scripts_test_only.ts @@ -0,0 +1,55 @@ +import { JsonRule } from "../../JsonRule.js"; +import { Project } from "../../project-model/index.js"; +import { Finding } from "../../report-model/index.js"; + +export class FN021014_PKG_scripts_test_only extends JsonRule { + constructor(private script: string) { + super(); + } + + get id(): string { + return 'FN021014'; + } + + get title(): string { + return 'package.json scripts.test-only'; + } + + get description(): string { + return 'Add package.json scripts.test-only property'; + } + + get resolution(): string { + return `{ + "scripts": { + "test-only": "${this.script}" + } +}`; + } + + get resolutionType(): string { + return 'json'; + } + + get severity(): string { + return 'Required'; + } + + get file(): string { + return './package.json'; + } + + visit(project: Project, findings: Finding[]): void { + if (!project.packageJson) { + return; + } + + if (!project.packageJson.scripts || + typeof project.packageJson.scripts !== 'object' || + !project.packageJson.scripts['test-only'] || + project.packageJson.scripts['test-only'] !== this.script) { + const node = this.getAstNodeFromFile(project.packageJson, 'scripts.test-only'); + this.addFindingWithPosition(findings, node); + } + } +} \ No newline at end of file diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN021015_PKG_scripts_trust_dev_cert.spec.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN021015_PKG_scripts_trust_dev_cert.spec.ts new file mode 100644 index 00000000000..97f227079f8 --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN021015_PKG_scripts_trust_dev_cert.spec.ts @@ -0,0 +1,73 @@ +import assert from 'assert'; +import { Project } from '../../project-model/index.js'; +import { Finding } from '../../report-model/index.js'; +import { FN021015_PKG_scripts_trust_dev_cert } from './FN021015_PKG_scripts_trust_dev_cert.js'; + +describe('FN021015_PKG_scripts_trust_dev_cert', () => { + let findings: Finding[]; + let rule: FN021015_PKG_scripts_trust_dev_cert; + + beforeEach(() => { + findings = []; + rule = new FN021015_PKG_scripts_trust_dev_cert('heft trust-dev-cert'); + }); + + it(`doesn't return notification if package.json is not available`, () => { + const project: Project = { + path: '/usr/tmp' + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it(`returns notification if scripts property is not defined`, () => { + const project: Project = { + path: '/usr/tmp', + packageJson: {} + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 1); + }); + + it(`returns notification if scripts.trust-dev-cert property is not defined`, () => { + const project: Project = { + path: '/usr/tmp', + packageJson: { + scripts: {} + } + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 1); + }); + + it(`returns notification if scripts.trust-dev-cert property is different than expected`, () => { + const project: Project = { + path: '/usr/tmp', + packageJson: { + scripts: { + 'trust-dev-cert': 'trust-cert' + } + } + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 1); + }); + + it(`returns correct node when scripts.trust-dev-cert is set to a string`, () => { + const project: Project = { + path: '/usr/tmp', + packageJson: { + scripts: { + 'trust-dev-cert': 'trust-cert' + }, + source: JSON.stringify({ + scripts: { + 'trust-dev-cert': 'trust-cert' + } + }, null, 2) + } + }; + rule.visit(project, findings); + assert.strictEqual(findings[0].occurrences[0].position?.line, 3); + }); +}); diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN021015_PKG_scripts_trust_dev_cert.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN021015_PKG_scripts_trust_dev_cert.ts new file mode 100644 index 00000000000..b22cfbae8b3 --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN021015_PKG_scripts_trust_dev_cert.ts @@ -0,0 +1,55 @@ +import { JsonRule } from "../../JsonRule.js"; +import { Project } from "../../project-model/index.js"; +import { Finding } from "../../report-model/index.js"; + +export class FN021015_PKG_scripts_trust_dev_cert extends JsonRule { + constructor(private script: string) { + super(); + } + + get id(): string { + return 'FN021015'; + } + + get title(): string { + return 'package.json scripts.trust-dev-cert'; + } + + get description(): string { + return 'Add package.json scripts.trust-dev-cert property'; + } + + get resolution(): string { + return `{ + "scripts": { + "trust-dev-cert": "${this.script}" + } +}`; + } + + get resolutionType(): string { + return 'json'; + } + + get severity(): string { + return 'Required'; + } + + get file(): string { + return './package.json'; + } + + visit(project: Project, findings: Finding[]): void { + if (!project.packageJson) { + return; + } + + if (!project.packageJson.scripts || + typeof project.packageJson.scripts !== 'object' || + !project.packageJson.scripts['trust-dev-cert'] || + project.packageJson.scripts['trust-dev-cert'] !== this.script) { + const node = this.getAstNodeFromFile(project.packageJson, 'scripts.trust-dev-cert'); + this.addFindingWithPosition(findings, node); + } + } +} \ No newline at end of file diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN021016_PKG_scripts_untrust_dev_cert.spec.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN021016_PKG_scripts_untrust_dev_cert.spec.ts new file mode 100644 index 00000000000..8a2f1bc56c9 --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN021016_PKG_scripts_untrust_dev_cert.spec.ts @@ -0,0 +1,73 @@ +import assert from 'assert'; +import { Project } from '../../project-model/index.js'; +import { Finding } from '../../report-model/index.js'; +import { FN021016_PKG_scripts_untrust_dev_cert } from './FN021016_PKG_scripts_untrust_dev_cert.js'; + +describe('FN021016_PKG_scripts_untrust_dev_cert', () => { + let findings: Finding[]; + let rule: FN021016_PKG_scripts_untrust_dev_cert; + + beforeEach(() => { + findings = []; + rule = new FN021016_PKG_scripts_untrust_dev_cert('heft untrust-dev-cert'); + }); + + it(`doesn't return notification if package.json is not available`, () => { + const project: Project = { + path: '/usr/tmp' + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it(`returns notification if scripts property is not defined`, () => { + const project: Project = { + path: '/usr/tmp', + packageJson: {} + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 1); + }); + + it(`returns notification if scripts.untrust-dev-cert property is not defined`, () => { + const project: Project = { + path: '/usr/tmp', + packageJson: { + scripts: {} + } + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 1); + }); + + it(`returns notification if scripts.untrust-dev-cert property is different than expected`, () => { + const project: Project = { + path: '/usr/tmp', + packageJson: { + scripts: { + 'untrust-dev-cert': 'untrust-cert' + } + } + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 1); + }); + + it(`returns correct node when scripts.untrust-dev-cert is set to a string`, () => { + const project: Project = { + path: '/usr/tmp', + packageJson: { + scripts: { + 'untrust-dev-cert': 'untrust-cert' + }, + source: JSON.stringify({ + scripts: { + 'untrust-dev-cert': 'untrust-cert' + } + }, null, 2) + } + }; + rule.visit(project, findings); + assert.strictEqual(findings[0].occurrences[0].position?.line, 3); + }); +}); diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN021016_PKG_scripts_untrust_dev_cert.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN021016_PKG_scripts_untrust_dev_cert.ts new file mode 100644 index 00000000000..0f983681ab5 --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN021016_PKG_scripts_untrust_dev_cert.ts @@ -0,0 +1,55 @@ +import { JsonRule } from "../../JsonRule.js"; +import { Project } from "../../project-model/index.js"; +import { Finding } from "../../report-model/index.js"; + +export class FN021016_PKG_scripts_untrust_dev_cert extends JsonRule { + constructor(private script: string) { + super(); + } + + get id(): string { + return 'FN021016'; + } + + get title(): string { + return 'package.json scripts.untrust-dev-cert'; + } + + get description(): string { + return 'Add package.json scripts.untrust-dev-cert property'; + } + + get resolution(): string { + return `{ + "scripts": { + "untrust-dev-cert": "${this.script}" + } +}`; + } + + get resolutionType(): string { + return 'json'; + } + + get severity(): string { + return 'Required'; + } + + get file(): string { + return './package.json'; + } + + visit(project: Project, findings: Finding[]): void { + if (!project.packageJson) { + return; + } + + if (!project.packageJson.scripts || + typeof project.packageJson.scripts !== 'object' || + !project.packageJson.scripts['untrust-dev-cert'] || + project.packageJson.scripts['untrust-dev-cert'] !== this.script) { + const node = this.getAstNodeFromFile(project.packageJson, 'scripts.untrust-dev-cert'); + this.addFindingWithPosition(findings, node); + } + } +} \ No newline at end of file diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name.spec.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name.spec.ts new file mode 100644 index 00000000000..8a9616d6a6e --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name.spec.ts @@ -0,0 +1,87 @@ +import assert from 'assert'; +import fs from 'fs'; +import sinon from 'sinon'; +import { sinonUtil } from '../../../../../../utils/sinonUtil.js'; +import { Project, TsFile } from '../../project-model/index.js'; +import { Finding } from '../../report-model/Finding.js'; +import { FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name } from './FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name.js'; + +describe('FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name', () => { + let findings: Finding[]; + let rule: FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name; + + afterEach(() => { + sinonUtil.restore([ + fs.existsSync, + fs.readFileSync + ]); + }); + + beforeEach(() => { + rule = new FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name(); + findings = []; + }); + + it('doesn\'t return notification if .eslintrc.js not found', () => { + sinon.stub(fs, 'existsSync').callsFake(() => false); + const project: Project = { + path: '/usr/tmp' + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it('doesn\'t return notification if .eslintrc.js is found but no nodes are present', () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + sinon.stub(fs, 'readFileSync').callsFake(() => ``); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it('doesn\'t return notification if .eslintrc.js is found but module is not present', () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + sinon.stub(fs, 'readFileSync').callsFake(() => `foo`); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it('file returned is ./.eslintrc.js when found', () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(rule.file, './.eslintrc.js'); + }); + + it(`doesn't return notification if @rushstack/import-requires-chunk-name property is present`, () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + sinon.stub(fs, 'readFileSync').callsFake(() => `export default { parserOptions: parserOptions: { tsconfigRootDir: __dirname }, { overrides: [ { rules: { '@rushstack/import-requires-chunk-name': 1 } } ] } }`); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it('returns notification if @rushstack/import-requires-chunk-name property is not present', () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + sinon.stub(fs, 'readFileSync').callsFake(() => `module.exports = { parserOptions: parserOptions: { tsconfigRootDir: __dirname }, { overrides: [ { rules: { } } ] } }`); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 1); + }); +}); diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name.ts new file mode 100644 index 00000000000..1b4061f20b9 --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name.ts @@ -0,0 +1,73 @@ +import os from 'os'; +import ts from 'typescript'; +import { Project } from '../../project-model/index.js'; +import { Finding, Occurrence } from '../../report-model/index.js'; +import { TsRule } from './TsRule.js'; + +export class FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name extends TsRule { + constructor() { + super(); + } + + get id(): string { + return 'FN025002'; + } + + get title(): string { + return '.eslintrc.js override rule @rushstack/import-requires-chunk-name'; + } + + get description(): string { + return `Add override rule @rushstack/import-requires-chunk-name in .eslintrc.js`; + } + + get resolution(): string { + return `// Require chunk names for dynamic imports in SPFx projects. https://www.npmjs.com/package/@rushstack/eslint-plugin + '@rushstack/import-requires-chunk-name': 1,${os.EOL}`; + } + + get resolutionType(): string { + return 'js'; + } + + get severity(): string { + return 'Required'; + } + + get file(): string { + return './.eslintrc.js'; + } + + visit(project: Project, findings: Finding[]): void { + if (!project.esLintRcJs) { + return; + } + + const nodes: ts.Node[] | undefined = project.esLintRcJs.nodes; + if (!nodes) { + return; + } + + const occurrences: Occurrence[] = []; + if (nodes + .filter(node => ts.isPropertyName(node)) + .map(node => node as ts.PropertyNameLiteral) + .filter(i => i.getText() === `'@rushstack/import-requires-chunk-name'`).length !== 0) { + return; + } + + const node = nodes + .map(node => node as ts.Identifier) + .find(i => i.text === 'rules'); + + if (!node) { + return; + } + + this.addOccurrence(this.resolution, project.esLintRcJs.path, project.path, node, occurrences); + + if (occurrences.length > 0) { + this.addFindingWithOccurrences(occurrences, findings); + } + } +} diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount.spec.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount.spec.ts new file mode 100644 index 00000000000..9756245a84e --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount.spec.ts @@ -0,0 +1,87 @@ +import assert from 'assert'; +import fs from 'fs'; +import sinon from 'sinon'; +import { sinonUtil } from '../../../../../../utils/sinonUtil.js'; +import { Project, TsFile } from '../../project-model/index.js'; +import { Finding } from '../../report-model/Finding.js'; +import { FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount } from './FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount.js'; + +describe('FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount', () => { + let findings: Finding[]; + let rule: FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount; + + afterEach(() => { + sinonUtil.restore([ + fs.existsSync, + fs.readFileSync + ]); + }); + + beforeEach(() => { + rule = new FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount(); + findings = []; + }); + + it('doesn\'t return notification if .eslintrc.js not found', () => { + sinon.stub(fs, 'existsSync').callsFake(() => false); + const project: Project = { + path: '/usr/tmp' + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it('doesn\'t return notification if .eslintrc.js is found but no nodes are present', () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + sinon.stub(fs, 'readFileSync').callsFake(() => ``); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it('doesn\'t return notification if .eslintrc.js is found but module is not present', () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + sinon.stub(fs, 'readFileSync').callsFake(() => `foo`); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it('file returned is ./.eslintrc.js when found', () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(rule.file, './.eslintrc.js'); + }); + + it(`doesn't return notification if @rushstack/pair-react-dom-render-unmount is present`, () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + sinon.stub(fs, 'readFileSync').callsFake(() => `export default { parserOptions: parserOptions: { tsconfigRootDir: __dirname }, { overrides: [ { rules: { '@rushstack/pair-react-dom-render-unmount': 1 } } ] } }`); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it('returns notification if @rushstack/pair-react-dom-render-unmount is not present', () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + sinon.stub(fs, 'readFileSync').callsFake(() => `module.exports = { parserOptions: parserOptions: { tsconfigRootDir: __dirname }, { overrides: [ { rules: { } } ] } }`); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 1); + }); +}); diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount.ts new file mode 100644 index 00000000000..5c808b8af3d --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount.ts @@ -0,0 +1,73 @@ +import os from 'os'; +import ts from 'typescript'; +import { Project } from '../../project-model/index.js'; +import { Finding, Occurrence } from '../../report-model/index.js'; +import { TsRule } from './TsRule.js'; + +export class FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount extends TsRule { + constructor() { + super(); + } + + get id(): string { + return 'FN025003'; + } + + get title(): string { + return '.eslintrc.js override rule @rushstack/pair-react-dom-render-unmount'; + } + + get description(): string { + return `Add override rule @rushstack/pair-react-dom-render-unmount in .eslintrc.js`; + } + + get resolution(): string { + return `// Ensure that React components rendered with ReactDOM.render() are unmounted with ReactDOM.unmountComponentAtNode(). https://www.npmjs.com/package/@rushstack/eslint-plugin + '@rushstack/pair-react-dom-render-unmount': 1,${os.EOL}`; + } + + get resolutionType(): string { + return 'js'; + } + + get severity(): string { + return 'Required'; + } + + get file(): string { + return './.eslintrc.js'; + } + + visit(project: Project, findings: Finding[]): void { + if (!project.esLintRcJs) { + return; + } + + const nodes: ts.Node[] | undefined = project.esLintRcJs.nodes; + if (!nodes) { + return; + } + + const occurrences: Occurrence[] = []; + if (nodes + .filter(node => ts.isPropertyName(node)) + .map(node => node as ts.PropertyNameLiteral) + .filter(i => i.getText() === `'@rushstack/pair-react-dom-render-unmount'`).length !== 0) { + return; + } + + const node = nodes + .map(node => node as ts.Identifier) + .find(i => i.text === 'rules'); + + if (!node) { + return; + } + + this.addOccurrence(this.resolution, project.esLintRcJs.path, project.path, node, occurrences); + + if (occurrences.length > 0) { + this.addFindingWithOccurrences(occurrences, findings); + } + } +} diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name.spec.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name.spec.ts new file mode 100644 index 00000000000..c6656c9475e --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name.spec.ts @@ -0,0 +1,71 @@ +import assert from 'assert'; +import fs from 'fs'; +import sinon from 'sinon'; +import { sinonUtil } from '../../../../../../utils/sinonUtil.js'; +import { Project, TsFile } from '../../project-model/index.js'; +import { Finding } from '../../report-model/Finding.js'; +import { FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name } from './FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name.js'; + +describe('FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name', () => { + let findings: Finding[]; + let rule: FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name; + + afterEach(() => { + sinonUtil.restore([ + fs.existsSync, + fs.readFileSync + ]); + }); + + beforeEach(() => { + rule = new FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name(); + findings = []; + }); + + it('file returned is ./.eslintrc.js', () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + assert.strictEqual(rule.file, './.eslintrc.js'); + }); + + it(`doesn't return notification if .eslintrc.js not found`, () => { + sinon.stub(fs, 'existsSync').callsFake(() => false); + const project: Project = { + path: '/usr/tmp' + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it(`doesn't return notification if .eslintrc.js is found but no nodes are present`, () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + sinon.stub(fs, 'readFileSync').callsFake(() => ``); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it(`doesn't return notification if @microsoft/spfx/import-requires-chunk-name property is absent`, () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + sinon.stub(fs, 'readFileSync').callsFake(() => `export default { parserOptions: parserOptions: { tsconfigRootDir: __dirname }, { overrides: [ { rules: { } } ] } }`); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it('returns notification if @microsoft/spfx/import-requires-chunk-name property is present', () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + sinon.stub(fs, 'readFileSync').callsFake(() => `module.exports = { parserOptions: parserOptions: { tsconfigRootDir: __dirname }, { overrides: [ { rules: { '@microsoft/spfx/import-requires-chunk-name': 1 } } ] } }`); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 1); + }); +}); diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name.ts new file mode 100644 index 00000000000..dd6484c4b53 --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name.ts @@ -0,0 +1,65 @@ +import ts from 'typescript'; +import { Project } from '../../project-model/index.js'; +import { Finding, Occurrence } from '../../report-model/index.js'; +import { TsRule } from './TsRule.js'; + +export class FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name extends TsRule { + constructor() { + super(); + } + + get id(): string { + return 'FN025004'; + } + + get title(): string { + return '.eslintrc.js override rule @microsoft/spfx/import-requires-chunk-name'; + } + + get description(): string { + return `Remove override rule @microsoft/spfx/import-requires-chunk-name in .eslintrc.js`; + } + + get resolution(): string { + return ''; + } + + get resolutionType(): string { + return 'js'; + } + + get severity(): string { + return 'Required'; + } + + get file(): string { + return './.eslintrc.js'; + } + + visit(project: Project, findings: Finding[]): void { + if (!project.esLintRcJs) { + return; + } + + const nodes: ts.Node[] | undefined = project.esLintRcJs.nodes; + if (!nodes) { + return; + } + + const occurrences: Occurrence[] = []; + const node = nodes + .filter(node => ts.isPropertyName(node)) + .map(node => node as ts.PropertyName) + .find(i => i.getText() === `'@microsoft/spfx/import-requires-chunk-name'`); + + if (!node) { + return; + } + + this.addOccurrence(this.resolution, project.esLintRcJs.path, project.path, node, occurrences); + + if (occurrences.length > 0) { + this.addFindingWithOccurrences(occurrences, findings); + } + } +} diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount.spec.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount.spec.ts new file mode 100644 index 00000000000..fe69fcc4e2e --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount.spec.ts @@ -0,0 +1,71 @@ +import assert from 'assert'; +import fs from 'fs'; +import sinon from 'sinon'; +import { sinonUtil } from '../../../../../../utils/sinonUtil.js'; +import { Project, TsFile } from '../../project-model/index.js'; +import { Finding } from '../../report-model/Finding.js'; +import { FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount } from './FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount.js'; + +describe('FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount', () => { + let findings: Finding[]; + let rule: FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount; + + afterEach(() => { + sinonUtil.restore([ + fs.existsSync, + fs.readFileSync + ]); + }); + + beforeEach(() => { + rule = new FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount(); + findings = []; + }); + + it('file returned is ./.eslintrc.js', () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + assert.strictEqual(rule.file, './.eslintrc.js'); + }); + + it(`doesn't return notification if .eslintrc.js not found`, () => { + sinon.stub(fs, 'existsSync').callsFake(() => false); + const project: Project = { + path: '/usr/tmp' + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it(`doesn't return notification if .eslintrc.js is found but no nodes are present`, () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + sinon.stub(fs, 'readFileSync').callsFake(() => ``); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it(`doesn't return notification if @microsoft/spfx/pair-react-dom-render-unmount property is absent`, () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + sinon.stub(fs, 'readFileSync').callsFake(() => `export default { parserOptions: parserOptions: { tsconfigRootDir: __dirname }, { overrides: [ { rules: { } } ] } }`); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 0); + }); + + it('returns notification if @microsoft/spfx/pair-react-dom-render-unmount property is present', () => { + sinon.stub(fs, 'existsSync').callsFake(() => true); + sinon.stub(fs, 'readFileSync').callsFake(() => `module.exports = { parserOptions: parserOptions: { tsconfigRootDir: __dirname }, { overrides: [ { rules: { '@microsoft/spfx/pair-react-dom-render-unmount': 1 } } ] } }`); + const project: Project = { + path: '/usr/tmp', + esLintRcJs: new TsFile('foo') + }; + rule.visit(project, findings); + assert.strictEqual(findings.length, 1); + }); +}); diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount.ts new file mode 100644 index 00000000000..a23a26557bd --- /dev/null +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount.ts @@ -0,0 +1,65 @@ +import ts from 'typescript'; +import { Project } from '../../project-model/index.js'; +import { Finding, Occurrence } from '../../report-model/index.js'; +import { TsRule } from './TsRule.js'; + +export class FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount extends TsRule { + constructor() { + super(); + } + + get id(): string { + return 'FN025005'; + } + + get title(): string { + return '.eslintrc.js override rule @microsoft/spfx/pair-react-dom-render-unmount'; + } + + get description(): string { + return `Remove override rule @microsoft/spfx/pair-react-dom-render-unmount in .eslintrc.js`; + } + + get resolution(): string { + return ''; + } + + get resolutionType(): string { + return 'js'; + } + + get severity(): string { + return 'Required'; + } + + get file(): string { + return './.eslintrc.js'; + } + + visit(project: Project, findings: Finding[]): void { + if (!project.esLintRcJs) { + return; + } + + const nodes: ts.Node[] | undefined = project.esLintRcJs.nodes; + if (!nodes) { + return; + } + + const occurrences: Occurrence[] = []; + const node = nodes + .filter(node => ts.isPropertyName(node)) + .map(node => node as ts.PropertyName) + .find(i => i.getText() === `'@microsoft/spfx/pair-react-dom-render-unmount'`); + + if (!node) { + return; + } + + this.addOccurrence(this.resolution, project.esLintRcJs.path, project.path, node, occurrences); + + if (occurrences.length > 0) { + this.addFindingWithOccurrences(occurrences, findings); + } + } +} diff --git a/src/m365/spfx/commands/project/project-upgrade/upgrade-1.22.0-beta.1.ts b/src/m365/spfx/commands/project/project-upgrade/upgrade-1.22.0-rc.0.ts similarity index 60% rename from src/m365/spfx/commands/project/project-upgrade/upgrade-1.22.0-beta.1.ts rename to src/m365/spfx/commands/project/project-upgrade/upgrade-1.22.0-rc.0.ts index ee7ec31be63..5c54bdfb338 100644 --- a/src/m365/spfx/commands/project/project-upgrade/upgrade-1.22.0-beta.1.ts +++ b/src/m365/spfx/commands/project/project-upgrade/upgrade-1.22.0-rc.0.ts @@ -25,18 +25,19 @@ import { FN002007_DEVDEP_ajv } from './rules/FN002007_DEVDEP_ajv.js'; import { FN002021_DEVDEP_rushstack_eslint_config } from './rules/FN002021_DEVDEP_rushstack_eslint_config.js'; import { FN002022_DEVDEP_microsoft_eslint_plugin_spfx } from './rules/FN002022_DEVDEP_microsoft_eslint_plugin_spfx.js'; import { FN002023_DEVDEP_microsoft_eslint_config_spfx } from './rules/FN002023_DEVDEP_microsoft_eslint_config_spfx.js'; +import { FN002026_DEVDEP_typescript } from './rules/FN002026_DEVDEP_typescript.js'; import { FN002029_DEVDEP_microsoft_rush_stack_compiler_5_3 } from './rules/FN002029_DEVDEP_microsoft_rush_stack_compiler_5_3.js'; import { FN002030_DEVDEP_microsoft_spfx_web_build_rig } from './rules/FN002030_DEVDEP_microsoft_spfx_web_build_rig.js'; import { FN002031_DEVDEP_rushstack_heft } from './rules/FN002031_DEVDEP_rushstack_heft.js'; import { FN002032_DEVDEP_typescript_eslint_parser } from './rules/FN002032_DEVDEP_typescript_eslint_parser.js'; -import { FN002033_DEVDEP_jest_junit } from './rules/FN002033_DEVDEP_jest_junit.js'; +import { FN002033_DEVDEP_css_loader } from './rules/FN002033_DEVDEP_css_loader.js'; +import { FN002034_DEVDEP_microsoft_spfx_heft_plugins } from './rules/FN002034_DEVDEP_microsoft_spfx_heft_plugins.js'; +import { FN002035_DEVDEP_types_heft_jest } from './rules/FN002035_DEVDEP_types_heft_jest.js'; import { FN010001_YORC_version } from './rules/FN010001_YORC_version.js'; import { FN010011_YORC_useGulp } from './rules/FN010011_YORC_useGulp.js'; import { FN015005_FILE_src_index_ts } from './rules/FN015005_FILE_src_index_ts.js'; import { FN015010_FILE_gulpfile_js } from './rules/FN015010_FILE_gulpfile_js.js'; import { FN015011_FILE_tsconfig_json } from './rules/FN015011_FILE_tsconfig_json.js'; -import { FN015012_FILE_config_heft_json } from './rules/FN015012_FILE_config_heft_json.js'; -import { FN015013_FILE_config_jest_config_json } from './rules/FN015013_FILE_config_jest_config_json.js'; import { FN015014_FILE_config_rig_json } from './rules/FN015014_FILE_config_rig_json.js'; import { FN015015_FILE_config_typescript_json } from './rules/FN015015_FILE_config_typescript_json.js'; import { FN020001_RES_types_react } from './rules/FN020001_RES_types_react.js'; @@ -51,155 +52,62 @@ import { FN021010_PKG_scripts_package_solution } from './rules/FN021010_PKG_scri import { FN021011_PKG_scripts_deploy_azure_storage } from './rules/FN021011_PKG_scripts_deploy_azure_storage.js'; import { FN021012_PKG_scripts_eject_webpack } from './rules/FN021012_PKG_scripts_eject_webpack.js'; import { FN021013_PKG_overrides_rushstack_heft } from './rules/FN021013_PKG_overrides_rushstack_heft.js'; +import { FN021014_PKG_scripts_test_only } from './rules/FN021014_PKG_scripts_test_only.js'; +import { FN021015_PKG_scripts_trust_dev_cert } from './rules/FN021015_PKG_scripts_trust_dev_cert.js'; +import { FN021016_PKG_scripts_untrust_dev_cert } from './rules/FN021016_PKG_scripts_untrust_dev_cert.js'; import { FN023003_GITIGNORE_libdts } from './rules/FN023003_GITIGNORE_libdts.js'; import { FN023004_GITIGNORE_libcommonjs } from './rules/FN023004_GITIGNORE_libcommonjs.js'; import { FN023005_GITIGNORE_libesm } from './rules/FN023005_GITIGNORE_libesm.js'; import { FN023006_GITIGNORE_jestoutput } from './rules/FN023006_GITIGNORE_jestoutput.js'; +import { FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name } from './rules/FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name.js'; +import { FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount } from './rules/FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount.js'; +import { FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name } from './rules/FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name.js'; +import { FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount } from './rules/FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount.js'; import { FN026001_CFG_SASS_schema } from './rules/FN026001_CFG_SASS_schema.js'; import { FN026002_CFG_SASS_extends } from './rules/FN026002_CFG_SASS_extends.js'; export default [ - new FN001001_DEP_microsoft_sp_core_library('1.22.0-beta.1'), - new FN001002_DEP_microsoft_sp_lodash_subset('1.22.0-beta.1'), - new FN001003_DEP_microsoft_sp_office_ui_fabric_core('1.22.0-beta.1'), - new FN001004_DEP_microsoft_sp_webpart_base('1.22.0-beta.1'), - new FN001011_DEP_microsoft_sp_dialog('1.22.0-beta.1'), - new FN001012_DEP_microsoft_sp_application_base('1.22.0-beta.1'), - new FN001014_DEP_microsoft_sp_listview_extensibility('1.22.0-beta.1'), - new FN001021_DEP_microsoft_sp_property_pane('1.22.0-beta.1'), - new FN001023_DEP_microsoft_sp_component_base('1.22.0-beta.1'), - new FN001024_DEP_microsoft_sp_diagnostics('1.22.0-beta.1'), - new FN001025_DEP_microsoft_sp_dynamic_data('1.22.0-beta.1'), - new FN001026_DEP_microsoft_sp_extension_base('1.22.0-beta.1'), - new FN001027_DEP_microsoft_sp_http('1.22.0-beta.1'), - new FN001028_DEP_microsoft_sp_list_subscription('1.22.0-beta.1'), - new FN001029_DEP_microsoft_sp_loader('1.22.0-beta.1'), - new FN001030_DEP_microsoft_sp_module_interfaces('1.22.0-beta.1'), - new FN001031_DEP_microsoft_sp_odata_types('1.22.0-beta.1'), - new FN001032_DEP_microsoft_sp_page_context('1.22.0-beta.1'), - new FN001013_DEP_microsoft_decorators('1.22.0-beta.1'), - new FN001034_DEP_microsoft_sp_adaptive_card_extension_base('1.22.0-beta.1'), - new FN002001_DEVDEP_microsoft_sp_build_web('1.22.0-beta.1', false), - new FN002002_DEVDEP_microsoft_sp_module_interfaces('1.22.0-beta.1'), + new FN001001_DEP_microsoft_sp_core_library('1.22.0-rc.0'), + new FN001002_DEP_microsoft_sp_lodash_subset('1.22.0-rc.0'), + new FN001003_DEP_microsoft_sp_office_ui_fabric_core('1.22.0-rc.0'), + new FN001004_DEP_microsoft_sp_webpart_base('1.22.0-rc.0'), + new FN001011_DEP_microsoft_sp_dialog('1.22.0-rc.0'), + new FN001012_DEP_microsoft_sp_application_base('1.22.0-rc.0'), + new FN001014_DEP_microsoft_sp_listview_extensibility('1.22.0-rc.0'), + new FN001021_DEP_microsoft_sp_property_pane('1.22.0-rc.0'), + new FN001023_DEP_microsoft_sp_component_base('1.22.0-rc.0'), + new FN001024_DEP_microsoft_sp_diagnostics('1.22.0-rc.0'), + new FN001025_DEP_microsoft_sp_dynamic_data('1.22.0-rc.0'), + new FN001026_DEP_microsoft_sp_extension_base('1.22.0-rc.0'), + new FN001027_DEP_microsoft_sp_http('1.22.0-rc.0'), + new FN001028_DEP_microsoft_sp_list_subscription('1.22.0-rc.0'), + new FN001029_DEP_microsoft_sp_loader('1.22.0-rc.0'), + new FN001030_DEP_microsoft_sp_module_interfaces('1.22.0-rc.0'), + new FN001031_DEP_microsoft_sp_odata_types('1.22.0-rc.0'), + new FN001032_DEP_microsoft_sp_page_context('1.22.0-rc.0'), + new FN001013_DEP_microsoft_decorators('1.22.0-rc.0'), + new FN001034_DEP_microsoft_sp_adaptive_card_extension_base('1.22.0-rc.0'), + new FN002001_DEVDEP_microsoft_sp_build_web('1.22.0-rc.0', false), + new FN002002_DEVDEP_microsoft_sp_module_interfaces('1.22.0-rc.0'), new FN002004_DEVDEP_gulp('4.0.2', false), new FN002007_DEVDEP_ajv('6.12.6', false), - new FN002021_DEVDEP_rushstack_eslint_config('4.3.0'), - new FN002022_DEVDEP_microsoft_eslint_plugin_spfx('1.22.0-beta.1'), - new FN002023_DEVDEP_microsoft_eslint_config_spfx('1.22.0-beta.1'), + new FN002021_DEVDEP_rushstack_eslint_config('4.5.2'), + new FN002022_DEVDEP_microsoft_eslint_plugin_spfx('1.22.0-rc.0'), + new FN002023_DEVDEP_microsoft_eslint_config_spfx('1.22.0-rc.0'), + new FN002026_DEVDEP_typescript('5.8.0'), new FN002029_DEVDEP_microsoft_rush_stack_compiler_5_3('0.1.0', false), - new FN002030_DEVDEP_microsoft_spfx_web_build_rig('1.22.0-beta.1'), - new FN002031_DEVDEP_rushstack_heft('0.73.6'), - new FN002032_DEVDEP_typescript_eslint_parser('8.26.1'), - new FN002033_DEVDEP_jest_junit('12.3.0'), - new FN010001_YORC_version('1.22.0-beta.1'), - new FN010011_YORC_useGulp(true), + new FN002030_DEVDEP_microsoft_spfx_web_build_rig('1.22.0-rc.0'), + new FN002031_DEVDEP_rushstack_heft('1.1.2'), + new FN002032_DEVDEP_typescript_eslint_parser('8.46.2'), + new FN002033_DEVDEP_css_loader('7.1.2'), + new FN002034_DEVDEP_microsoft_spfx_heft_plugins('1.22.0-rc.0'), + new FN002035_DEVDEP_types_heft_jest('1.0.2'), + new FN010001_YORC_version('1.22.0-rc.0'), + new FN010011_YORC_useGulp(false), new FN015005_FILE_src_index_ts(false), new FN015010_FILE_gulpfile_js(false), new FN015011_FILE_tsconfig_json(true, `{ - "extends": "./node_modules/@microsoft/spfx-web-build-rig/profiles/default/tsconfig-base.json", - "compilerOptions": { - "skipLibCheck": true - } -} -`), - new FN015012_FILE_config_heft_json(true, `{ - "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", - - "extends": "@microsoft/spfx-web-build-rig/profiles/default/config/heft.json", - - "phasesByName": { - "build": { - "tasksByName": { - // copies the localization js files to the lib folder - "copy-strings": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft", - "pluginName": "copy-files-plugin", - "options": { - "copyOperations": [ - { - "sourcePath": "./src", - "destinationFolders": ["./lib", "./lib-commonjs"], - "fileExtensions": [".js"] - } - ] - } - } - } - } - }, - "eject-webpack": { - "tasksByName": { - "eject-webpack": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft", - "pluginName": "run-script-plugin", - "options": { - "scriptPath": "node_modules/@microsoft/spfx-heft-plugins/lib-commonjs/plugins/ejectWebpackPlugin/EjectWebpackPlugin.js" - } - } - } - } - } - } -} -`), - new FN015013_FILE_config_jest_config_json(true, `{ - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", - - "roots": ["/lib"], - - "testMatch": ["/lib/**/*.test.js"], - - "collectCoverageFrom": [ - "lib/**/*.js", - "!lib/**/*.d.ts", - "!lib/**/*.test.js", - "!lib/**/test/**", - "!lib/**/__tests__/**", - "!lib/**/__fixtures__/**", - "!lib/**/__mocks__/**" - ], - - "transformIgnorePatterns": ["[\\/]lib[\\/](.*)\\.js$"], - - "testEnvironment": "jest-environment-jsdom", - "testEnvironmentOptions": { - "customExportConditions": ["require", "node", "umd"] - }, - - "moduleFileExtensions": ["cjs", "js", "json", "node", "css"], - - // Enable code coverage for Jest - "collectCoverage": true, - "coverageDirectory": "/jest-output/coverage", - "coverageReporters": [ - [ - "cobertura", - { - "file": "cobertura/cobertura-coverage.xml" - } - ], - "html", - "json-summary" - ], - - // Use v8 coverage provider to avoid Babel - "coverageProvider": "v8", - - // Enable junit reporting for Jest - "reporters": [ - "default", - [ - "jest-junit", - { - "outputDirectory": "/jest-output", - "outputName": "JUnit.xml", // QTest expects this casing. - "classNameTemplate": "{classname} > ", - "titleTemplate": "{title} ({filepath})" - } - ] - ] + "extends": "./node_modules/@microsoft/spfx-web-build-rig/profiles/default/tsconfig-base.json" } `), new FN015014_FILE_config_rig_json(true, `{ @@ -222,7 +130,7 @@ export default [ `), new FN020001_RES_types_react('17.0.45'), new FN021001_PKG_main(false), - new FN021004_PKG_scripts_build('heft test --clean'), + new FN021004_PKG_scripts_build('heft build --clean'), new FN021005_PKG_scripts_test('heft test'), new FN021006_PKG_scripts_clean('heft clean'), new FN021007_PKG_scripts_deploy('heft dev-deploy'), @@ -231,11 +139,18 @@ export default [ new FN021010_PKG_scripts_package_solution('heft package-solution'), new FN021011_PKG_scripts_deploy_azure_storage('heft deploy-azure-storage'), new FN021012_PKG_scripts_eject_webpack('heft eject-webpack'), - new FN021013_PKG_overrides_rushstack_heft('0.73.6'), + new FN021013_PKG_overrides_rushstack_heft('1.1.2'), + new FN021014_PKG_scripts_test_only('heft run --only test --'), + new FN021015_PKG_scripts_trust_dev_cert('heft trust-dev-cert'), + new FN021016_PKG_scripts_untrust_dev_cert('heft untrust-dev-cert'), new FN023003_GITIGNORE_libdts(), new FN023004_GITIGNORE_libcommonjs(), new FN023005_GITIGNORE_libesm(), new FN023006_GITIGNORE_jestoutput(), + new FN025002_ESLINTRCJS_rushstack_import_requires_chunk_name(), + new FN025003_ESLINTRCJS_rushstack_pair_react_dom_render_unmount(), + new FN025004_ESLINTRCJS_microsoft_spfx_import_requires_chunk_name(), + new FN025005_ESLINTRCJS_microsoft_spfx_pair_react_dom_render_unmount(), new FN026001_CFG_SASS_schema('https://developer.microsoft.com/json-schemas/heft/v0/heft-sass-plugin.schema.json'), new FN026002_CFG_SASS_extends('@microsoft/spfx-web-build-rig/profiles/default/config/sass.json') ]; diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/.npmignore b/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/.npmignore deleted file mode 100644 index ae0b487c075..00000000000 --- a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/.npmignore +++ /dev/null @@ -1,16 +0,0 @@ -!dist -config - -gulpfile.js - -release -src -temp - -tsconfig.json -tslint.json - -*.log - -.yo-rc.json -.vscode diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/heft.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/heft.json deleted file mode 100644 index 1045bb0ff6e..00000000000 --- a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/heft.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", - - "extends": "@microsoft/spfx-web-build-rig/profiles/default/config/heft.json", - - "phasesByName": { - "build": { - "tasksByName": { - // copies the localization js files to the lib folder - "copy-strings": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft", - "pluginName": "copy-files-plugin", - "options": { - "copyOperations": [ - { - "sourcePath": "./src", - "destinationFolders": ["./lib", "./lib-commonjs"], - "fileExtensions": [".js"] - } - ] - } - } - } - } - }, - "eject-webpack": { - "tasksByName": { - "eject-webpack": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft", - "pluginName": "run-script-plugin", - "options": { - "scriptPath": "node_modules/@microsoft/spfx-heft-plugins/lib-commonjs/plugins/ejectWebpackPlugin/EjectWebpackPlugin.js" - } - } - } - } - } - } -} diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/jest.config.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/jest.config.json deleted file mode 100644 index 00354e3a687..00000000000 --- a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/jest.config.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", - - "roots": ["/lib"], - - "testMatch": ["/lib/**/*.test.js"], - - "collectCoverageFrom": [ - "lib/**/*.js", - "!lib/**/*.d.ts", - "!lib/**/*.test.js", - "!lib/**/test/**", - "!lib/**/__tests__/**", - "!lib/**/__fixtures__/**", - "!lib/**/__mocks__/**" - ], - - "transformIgnorePatterns": ["[\\/]lib[\\/](.*)\\.js$"], - - "testEnvironment": "jest-environment-jsdom", - "testEnvironmentOptions": { - "customExportConditions": ["require", "node", "umd"] - }, - - "moduleFileExtensions": ["cjs", "js", "json", "node", "css"], - - // Enable code coverage for Jest - "collectCoverage": true, - "coverageDirectory": "/jest-output/coverage", - "coverageReporters": [ - [ - "cobertura", - { - "file": "cobertura/cobertura-coverage.xml" - } - ], - "html", - "json-summary" - ], - - // Use v8 coverage provider to avoid Babel - "coverageProvider": "v8", - - // Enable junit reporting for Jest - "reporters": [ - "default", - [ - "jest-junit", - { - "outputDirectory": "/jest-output", - "outputName": "JUnit.xml", // QTest expects this casing. - "classNameTemplate": "{classname} > ", - "titleTemplate": "{title} ({filepath})" - } - ] - ] -} diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/package.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/package.json deleted file mode 100644 index 640cb3b192c..00000000000 --- a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "spfx-webpart-react", - "version": "0.0.1", - "private": true, - "engines": { - "node": ">=22.14.0 < 23.0.0" - }, - "scripts": { - "build": "heft test --clean", - "test": "heft test", - "clean": "heft clean", - "deploy": "heft dev-deploy", - "start": "heft start --clean", - "build-watch": "heft build --lite", - "package-solution": "heft package-solution", - "deploy-azure-storage": "heft deploy-azure-storage", - "eject-webpack": "heft eject-webpack" - }, - "dependencies": { - "tslib": "2.3.1", - "react": "17.0.1", - "react-dom": "17.0.1", - "@fluentui/react": "^8.106.4", - "@microsoft/sp-core-library": "1.22.0-beta.1", - "@microsoft/sp-component-base": "1.22.0-beta.1", - "@microsoft/sp-property-pane": "1.22.0-beta.1", - "@microsoft/sp-webpart-base": "1.22.0-beta.1", - "@microsoft/sp-lodash-subset": "1.22.0-beta.1", - "@microsoft/sp-office-ui-fabric-core": "1.22.0-beta.1" - }, - "devDependencies": { - "@rushstack/eslint-config": "4.3.0", - "@microsoft/eslint-plugin-spfx": "1.22.0-beta.1", - "@microsoft/eslint-config-spfx": "1.22.0-beta.1", - "@microsoft/spfx-web-build-rig": "1.22.0-beta.1", - "@rushstack/heft": "0.73.6", - "@types/webpack-env": "~1.15.2", - "@typescript-eslint/parser": "8.26.1", - "eslint": "8.57.1", - "jest-junit": "12.3.0", - "typescript": "5.3.3", - "@types/react": "17.0.45", - "@types/react-dom": "17.0.17", - "eslint-plugin-react-hooks": "4.3.0", - "@microsoft/sp-module-interfaces": "1.22.0-beta.1" - }, - "overrides": { - "@rushstack/heft": "0.73.6" - }, - "resolutions": { - "@types/react": "17.0.45" - } -} diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/tsconfig.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/tsconfig.json deleted file mode 100644 index 4a048137862..00000000000 --- a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "./node_modules/@microsoft/spfx-web-build-rig/profiles/default/tsconfig-base.json", - "compilerOptions": { - "skipLibCheck": true - } -} diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/.eslintrc.js b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/.eslintrc.js similarity index 97% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/.eslintrc.js rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/.eslintrc.js index 14c96f03215..a790d607622 100644 --- a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/.eslintrc.js +++ b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/.eslintrc.js @@ -16,6 +16,10 @@ module.exports = { '@rushstack/no-new-null': 1, // Require Jest module mocking APIs to be called before any other statements in their code block. https://www.npmjs.com/package/@rushstack/eslint-plugin '@rushstack/hoist-jest-mock': 1, + // Require chunk names for dynamic imports in SPFx projects. https://www.npmjs.com/package/@rushstack/eslint-plugin + '@rushstack/import-requires-chunk-name': 1, + // Ensure that React components rendered with ReactDOM.render() are unmounted with ReactDOM.unmountComponentAtNode(). https://www.npmjs.com/package/@rushstack/eslint-plugin + '@rushstack/pair-react-dom-render-unmount': 1, // Require regular expressions to be constructed from string constants rather than dynamically building strings at runtime. https://www.npmjs.com/package/@rushstack/eslint-plugin-security '@rushstack/security/no-unsafe-regexp': 1, // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json @@ -288,9 +292,7 @@ module.exports = { // ==================================================================== // @microsoft/eslint-plugin-spfx // ==================================================================== - '@microsoft/spfx/import-requires-chunk-name': 1, '@microsoft/spfx/no-require-ensure': 2, - '@microsoft/spfx/pair-react-dom-render-unmount': 1 } }, { diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/.gitignore b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/.gitignore similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/.gitignore rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/.gitignore diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/.npmignore b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/.npmignore new file mode 100644 index 00000000000..b2625b818a4 --- /dev/null +++ b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/.npmignore @@ -0,0 +1,22 @@ +# Ignore everything by default +** + +# Use negative patterns to bring back the specific things we want to publish +!/bin/** +!/dist/** +!/EULA/** +!/lib/** +!ThirdPartyNotice.txt + +# Ignore certain files in the above folders +/dist/*.stats.* +/dist/**/*.js.map +/lib/**/*.js.map +/lib/**/test/** + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README (and its variants) +# CHANGELOG (and its variants) +# LICENSE / LICENCE diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/.vscode/settings.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/.vscode/settings.json similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/.vscode/settings.json rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/.vscode/settings.json diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/.yo-rc.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/.yo-rc.json similarity index 80% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/.yo-rc.json rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/.yo-rc.json index fc9ed019df9..80bb82a0439 100644 --- a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/.yo-rc.json +++ b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/.yo-rc.json @@ -1,13 +1,13 @@ { "@microsoft/generator-sharepoint": { "useGulp": false, - "plusBeta": true, + "plusBeta": false, "isCreatingSolution": true, "nodeVersion": "22.14.0", "sdksVersions": {}, - "version": "1.22.0-beta.1", + "version": "1.22.0-rc.0", "libraryName": "spfx-webpart-react", - "libraryId": "e4bb2a99-7d22-4f50-9dc4-031e34a12dcd", + "libraryId": "8bd9da99-4dcf-47bc-9263-5ff013bb3b6b", "environment": "spo", "packageManager": "npm", "solutionName": "spfx-webpart-react", diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/README.md b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/README.md similarity index 97% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/README.md rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/README.md index d5fbae6b215..7599eccaa37 100644 --- a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/README.md +++ b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/README.md @@ -8,7 +8,7 @@ Short summary on functionality and used technologies. ## Used SharePoint Framework Version -![version](https://img.shields.io/badge/version-1.22.0--beta.1-yellow.svg) +![version](https://img.shields.io/badge/version-1.22.0--rc.0-yellow.svg) ## Applies to diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/config.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/config.json similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/config.json rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/config.json diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/deploy-azure-storage.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/deploy-azure-storage.json similarity index 99% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/deploy-azure-storage.json rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/deploy-azure-storage.json index 49ffcef46e8..32eb9c2ff90 100644 --- a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/deploy-azure-storage.json +++ b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/deploy-azure-storage.json @@ -4,4 +4,4 @@ "account": "", "container": "spfx-webpart-react", "accessKey": "" -} \ No newline at end of file +} diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/package-solution.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/package-solution.json similarity index 87% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/package-solution.json rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/package-solution.json index 74cc42bb631..cd02120c2d7 100644 --- a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/package-solution.json +++ b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/package-solution.json @@ -2,7 +2,7 @@ "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json", "solution": { "name": "spfx-webpart-react-client-side-solution", - "id": "e4bb2a99-7d22-4f50-9dc4-031e34a12dcd", + "id": "8bd9da99-4dcf-47bc-9263-5ff013bb3b6b", "version": "1.0.0.0", "includeClientSideAssets": true, "skipFeatureDeployment": true, @@ -12,7 +12,7 @@ "websiteUrl": "", "privacyUrl": "", "termsOfUseUrl": "", - "mpnId": "Undefined-1.22.0-beta.1" + "mpnId": "Undefined-1.22.0-rc.0" }, "metadata": { "shortDescription": { @@ -29,7 +29,7 @@ { "title": "spfx-webpart-react Feature", "description": "The feature that activates elements of the spfx-webpart-react solution.", - "id": "2e6efda9-8a25-4ab8-b952-70fafc851380", + "id": "f0f5ac39-6eca-41a3-8249-3df919856c50", "version": "1.0.0.0" } ] diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/rig.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/rig.json similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/rig.json rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/rig.json diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/sass.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/sass.json similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/sass.json rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/sass.json diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/serve.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/serve.json similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/serve.json rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/serve.json diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/typescript.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/typescript.json similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/typescript.json rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/typescript.json diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/write-manifests.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/write-manifests.json similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/config/write-manifests.json rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/config/write-manifests.json diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/package.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/package.json new file mode 100644 index 00000000000..513df73cb3c --- /dev/null +++ b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/package.json @@ -0,0 +1,58 @@ +{ + "name": "spfx-webpart-react", + "version": "0.0.1", + "private": true, + "engines": { + "node": ">=22.14.0 < 23.0.0" + }, + "scripts": { + "build": "heft build --clean", + "test": "heft test", + "test-only": "heft run --only test --", + "clean": "heft clean", + "deploy": "heft dev-deploy", + "start": "heft start --clean", + "build-watch": "heft build --lite", + "package-solution": "heft package-solution", + "deploy-azure-storage": "heft deploy-azure-storage", + "eject-webpack": "heft eject-webpack", + "trust-dev-cert": "heft trust-dev-cert", + "untrust-dev-cert": "heft untrust-dev-cert" + }, + "dependencies": { + "tslib": "2.3.1", + "react": "17.0.1", + "react-dom": "17.0.1", + "@fluentui/react": "^8.106.4", + "@microsoft/sp-core-library": "1.22.0-rc.0", + "@microsoft/sp-component-base": "1.22.0-rc.0", + "@microsoft/sp-property-pane": "1.22.0-rc.0", + "@microsoft/sp-webpart-base": "1.22.0-rc.0", + "@microsoft/sp-lodash-subset": "1.22.0-rc.0", + "@microsoft/sp-office-ui-fabric-core": "1.22.0-rc.0" + }, + "devDependencies": { + "@rushstack/eslint-config": "4.5.2", + "@microsoft/eslint-plugin-spfx": "1.22.0-rc.0", + "@microsoft/eslint-config-spfx": "1.22.0-rc.0", + "@microsoft/spfx-web-build-rig": "1.22.0-rc.0", + "@microsoft/spfx-heft-plugins": "1.22.0-rc.0", + "@rushstack/heft": "1.1.2", + "@types/heft-jest": "1.0.2", + "@types/webpack-env": "~1.15.2", + "@typescript-eslint/parser": "8.46.2", + "css-loader": "~7.1.2", + "eslint": "8.57.1", + "typescript": "~5.8.0", + "@types/react": "17.0.45", + "@types/react-dom": "17.0.17", + "eslint-plugin-react-hooks": "4.3.0", + "@microsoft/sp-module-interfaces": "1.22.0-rc.0" + }, + "overrides": { + "@rushstack/heft": "1.1.2" + }, + "resolutions": { + "@types/react": "17.0.45" + } +} diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/HelloWorldWebPart.manifest.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/HelloWorldWebPart.manifest.json similarity index 95% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/HelloWorldWebPart.manifest.json rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/HelloWorldWebPart.manifest.json index d016ecb1646..70c680a09af 100644 --- a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/HelloWorldWebPart.manifest.json +++ b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/HelloWorldWebPart.manifest.json @@ -1,6 +1,6 @@ { "$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json", - "id": "2652b91d-cc42-49f7-8c04-7cb4baf3eb7b", + "id": "18e7496d-3cd2-4df7-b0ae-65780bc46682", "alias": "HelloWorldWebPart", "componentType": "WebPart", diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/HelloWorldWebPart.ts b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/HelloWorldWebPart.ts similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/HelloWorldWebPart.ts rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/HelloWorldWebPart.ts diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/assets/welcome-dark.png b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/assets/welcome-dark.png similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/assets/welcome-dark.png rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/assets/welcome-dark.png diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/assets/welcome-light.png b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/assets/welcome-light.png similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/assets/welcome-light.png rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/assets/welcome-light.png diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/components/HelloWorld.module.scss b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/components/HelloWorld.module.scss similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/components/HelloWorld.module.scss rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/components/HelloWorld.module.scss diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/components/HelloWorld.tsx b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/components/HelloWorld.tsx similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/components/HelloWorld.tsx rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/components/HelloWorld.tsx diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/components/IHelloWorldProps.ts b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/components/IHelloWorldProps.ts similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/components/IHelloWorldProps.ts rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/components/IHelloWorldProps.ts diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/loc/en-us.js b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/loc/en-us.js similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/loc/en-us.js rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/loc/en-us.js diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/loc/mystrings.d.ts b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/loc/mystrings.d.ts similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/src/webparts/helloWorld/loc/mystrings.d.ts rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/src/webparts/helloWorld/loc/mystrings.d.ts diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/teams/2652b91d-cc42-49f7-8c04-7cb4baf3eb7b_color.png b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/teams/18e7496d-3cd2-4df7-b0ae-65780bc46682_color.png similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/teams/2652b91d-cc42-49f7-8c04-7cb4baf3eb7b_color.png rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/teams/18e7496d-3cd2-4df7-b0ae-65780bc46682_color.png diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/teams/2652b91d-cc42-49f7-8c04-7cb4baf3eb7b_outline.png b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/teams/18e7496d-3cd2-4df7-b0ae-65780bc46682_outline.png similarity index 100% rename from src/m365/spfx/commands/project/test-projects/spfx-1220-beta.1-webpart-react/teams/2652b91d-cc42-49f7-8c04-7cb4baf3eb7b_outline.png rename to src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/teams/18e7496d-3cd2-4df7-b0ae-65780bc46682_outline.png diff --git a/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/tsconfig.json b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/tsconfig.json new file mode 100644 index 00000000000..ee582d516cd --- /dev/null +++ b/src/m365/spfx/commands/project/test-projects/spfx-1220-rc.0-webpart-react/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/@microsoft/spfx-web-build-rig/profiles/default/tsconfig-base.json" +} diff --git a/src/m365/spfx/commands/spfx-doctor.spec.ts b/src/m365/spfx/commands/spfx-doctor.spec.ts index 393bb5e2693..4e12bed1977 100644 --- a/src/m365/spfx/commands/spfx-doctor.spec.ts +++ b/src/m365/spfx/commands/spfx-doctor.spec.ts @@ -905,7 +905,7 @@ describe(commands.DOCTOR, () => { try { getPackageVersionSpy = sinon.spy(command as any, 'getPackageVersion'); - await command.action(logger, { options: { spfxVersion: '1.22.0-beta.1' } }); + await command.action(logger, { options: { spfxVersion: '1.22.0-rc.0' } }); assert(getPackageVersionSpy.neverCalledWith('gulp-cli')); } finally { diff --git a/src/m365/spfx/commands/spfx-doctor.ts b/src/m365/spfx/commands/spfx-doctor.ts index 59f19dde7be..9362c5fd7ef 100644 --- a/src/m365/spfx/commands/spfx-doctor.ts +++ b/src/m365/spfx/commands/spfx-doctor.ts @@ -641,15 +641,15 @@ class SpfxDoctorCommand extends BaseProjectCommand { fix: 'npm i -g yo@5' } }, - '1.22.0-beta.1': { + '1.22.0-rc.0': { node: { range: '>=22.14.0 < 23.0.0', fix: 'Install Node.js >=22.14.0 < 23.0.0' }, sp: SharePointVersion.SPO, yo: { - range: '^4 || ^5', - fix: 'npm i -g yo@5' + range: '^4 || ^5 || ^6', + fix: 'npm i -g yo@6' } } };