Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add migration scripts for bootstrap dependency removal #19926

Merged
merged 29 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a90817f
create migration script for bootstrap dep removal
kpawelczak Jan 23, 2025
66bc641
update
kpawelczak Jan 23, 2025
0788078
revert config version
kpawelczak Jan 23, 2025
27fd745
Update dependencies.json
kpawelczak Jan 23, 2025
2c483a4
Merge branch 'develop' into feature/CXSPA-9283
kpawelczak Jan 23, 2025
3e495e3
Update add-imports-in-libraries.ts
kpawelczak Jan 23, 2025
7f4189b
Update bootstrap.ts
kpawelczak Jan 23, 2025
5427b37
Add license header
github-actions[bot] Jan 23, 2025
e68f3ec
Merge branch 'develop' into feature/CXSPA-9283
kpawelczak Jan 23, 2025
e491921
fix sonar
kpawelczak Jan 24, 2025
c31bda2
Merge branch 'develop' into feature/CXSPA-9283
kpawelczak Jan 24, 2025
c18e399
Update replace-bootstrap-imports.ts
kpawelczak Jan 24, 2025
431cf26
Merge branch 'develop' into feature/CXSPA-9283
kpawelczak Jan 24, 2025
503948e
Merge branch 'feature/CXSPA-9283' of https://github.com/SAP/spartacus…
kpawelczak Jan 24, 2025
d99410b
remove bootstrap imports from feature libs
kpawelczak Jan 30, 2025
1c0663e
Merge branch 'develop' into feature/CXSPA-9283
kpawelczak Jan 30, 2025
329b0c4
Update projects/schematics/src/migrations/2212_32/bootstrap/replace-b…
kpawelczak Jan 31, 2025
a691d73
Update projects/schematics/src/migrations/2212_32/bootstrap/replace-b…
kpawelczak Jan 31, 2025
1c84280
Update projects/schematics/src/migrations/migrations.json
kpawelczak Jan 31, 2025
f70e33e
Update projects/schematics/src/migrations/2212_32/bootstrap/bootstrap.ts
kpawelczak Jan 31, 2025
16f5c3e
Merge branch 'develop' into feature/CXSPA-9283
pawelfras Jan 31, 2025
6cb6564
update uninstall bootstrap function
kpawelczak Feb 3, 2025
37302a4
Merge branch 'feature/CXSPA-9283' of https://github.com/SAP/spartacus…
kpawelczak Feb 3, 2025
2fca035
Merge branch 'develop' into feature/CXSPA-9283
kpawelczak Feb 3, 2025
5821cae
fix sonar error
kpawelczak Feb 3, 2025
fbc3bf1
Merge branch 'feature/CXSPA-9283' of https://github.com/SAP/spartacus…
kpawelczak Feb 3, 2025
24ec75f
Merge branch 'develop' into feature/CXSPA-9283
Platonn Feb 3, 2025
739a837
update logs
kpawelczak Feb 4, 2025
6e5088e
fix uninstall command logs
kpawelczak Feb 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 6 additions & 32 deletions docs/migration/2211_35/bootstrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@
2. Update `styles.scss`
Modify the `styles.scss` file to integrate Spartacus styles along with Bootstrap. Proper import order is critical for
styles to be applied correctly.

### Steps to Update:

1. Place the following import for styles-config at the top of the file:
```@import 'styles-config';```
```@import 'styles-config';```
2. Add Spartacus core styles first. Importing Spartacus styles before Bootstrap ensures core styles load as a
priority.
3. Follow this by importing Bootstrap styles using the Bootstrap copy provided by Spartacus. Ensure the order of
Bootstrap imports matches the sequence below for consistency.
4. Conclude with the Spartacus index styles.


Final file structure should look like this:
Final file structure should look like this:

```styles.scss
// ORDER IMPORTANT: Spartacus core first
Expand All @@ -42,41 +43,14 @@

@import '@spartacus/styles/index';
```

3. Individual imports.
If your application directly imports specific Bootstrap classes in any of your stylesheets, replace those imports with the corresponding Spartacus imports. For example:

```
// Original import
@import '~bootstrap/scss/reboot';

// Replace with
@import '@spartacus/styles/vendor/bootstrap/scss/reboot';
```

4. Some libraries have stopped importing Bootstrap-related styles. Instead, these styles should now be imported directly within the application. For example, the `cart.scss` file should include the following imports:
```scss
// original imports
@import '../styles-config';
@import '@spartacus/cart';
```

```scss
// new imports
@import '../styles-config';
@import '@spartacus/cart';

@import '@spartacus/styles/vendor/bootstrap/scss/functions';
@import '@spartacus/styles/vendor/bootstrap/scss/variables';
@import '@spartacus/styles/vendor/bootstrap/scss/_mixins';
```
Affected libraries:
- cart
- checkout
- organization
- pick-up-in-store
- product
- product-multi-dimensional
- qualtrics
- quote
- storefinder
- epd-visualization
- opf
131 changes: 131 additions & 0 deletions projects/schematics/src/migrations/2211_35/bootstrap/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* SPDX-FileCopyrightText: 2025 SAP Spartacus team <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0
*/

import {
chain,
Rule,
SchematicContext,
Tree,
} from '@angular-devkit/schematics';
import { replaceBootstrapImports } from './replace-bootstrap-imports';
import { spawn } from 'node:child_process';

export function migrate(): Rule {
return (tree: Tree, context: SchematicContext) => {
return chain([
uninstallBootstrap(),
updateMainStylesFileImports(),
replaceBootstrapImports(),
])(tree, context);
};
}

function uninstallBootstrap(): Rule {
return (tree: Tree, context: SchematicContext) => {
return async () => {
// Detect the package manager
let packageManager = '';
if (tree.exists('yarn.lock')) {
packageManager = 'yarn';
} else if (tree.exists('pnpm-lock.yaml')) {
packageManager = 'pnpm';
} else if (tree.exists('package-lock.json')) {
packageManager = 'npm';
}

let uninstallCommand = '';
if (packageManager === 'yarn') {
uninstallCommand = 'yarn remove bootstrap';
} else if (packageManager === 'pnpm') {
uninstallCommand = 'pnpm remove bootstrap';
} else if (packageManager === 'npm') {
uninstallCommand = 'npm uninstall bootstrap';
} else {
context.logger.warn(
'Could not detect a package manager. Please uninstall Bootstrap manually.'
);
return;
}

context.logger.info(`Running uninstall command: ${uninstallCommand}`);

await new Promise<void>((resolve) => {
const child = spawn(uninstallCommand, { shell: true });

child.on('close', (code) => {
if (code === 0) {
context.logger.info('Bootstrap uninstalled successfully.');
resolve();
} else {
context.logger.error(
`Bootstrap uninstall failed with exit code ${code}. Please uninstall Bootstrap manually.`
);
resolve();
}
});
});
};
};
}

function updateMainStylesFileImports(): Rule {
return (tree: Tree, context: SchematicContext) => {
const filePath = 'src/styles.scss';

if (!tree.exists(filePath)) {
context.logger.warn(`File ${filePath} does not exist.`);
return tree;
}

const fileContent = tree.read(filePath)?.toString('utf-8');

if (!fileContent) {
context.logger.warn(`File ${filePath} is empty or could not be read.`);
return tree;
}

context.logger.info(`Updating Bootstrap imports in '${filePath}'...`);

const styleImportsToInsert =
`@import 'styles-config';\n` +
`\n// ORDER IMPORTANT: Spartacus core first\n` +
`@import '@spartacus/styles/scss/core';\n\n` +
`// ORDER IMPORTANT: Copy of Bootstrap files next\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/reboot';\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/type';\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/grid';\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/utilities';\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/transitions';\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/dropdown';\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/card';\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/nav';\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/buttons';\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/forms';\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/custom-forms';\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/modal';\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/close';\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/alert';\n` +
`@import '@spartacus/styles/vendor/bootstrap/scss/tooltip';\n\n` +
`// ORDER IMPORTANT: Spartacus styles last\n` +
`@import '@spartacus/styles/index';\n`;

const updatedContent = fileContent
.replace(
/\/\* You can add global styles to this file, and also import other style files \*\//g,
''
)
.replace(/@import\s+['"]@spartacus\/styles\/index['"];/g, '')
.replace(/@import ['"]styles-config['"];/g, styleImportsToInsert);

tree.overwrite(filePath, updatedContent);

context.logger.info(
`Bootstrap imports updated successfully in '${filePath}'.`
);

return tree;
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/*
* SPDX-FileCopyrightText: 2025 SAP Spartacus team <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0
*/

import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';

const bootstrapImportsToReplace = [
{
find: 'bootstrap/scss/alert',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/alert',
},
{
find: 'bootstrap/scss/badge',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/badge',
},
{
find: 'bootstrap/scss/breadcrumb',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/breadcrumb',
},
{
find: 'bootstrap/scss/button-group',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/button-group',
},
{
find: 'bootstrap/scss/buttons',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/buttons',
},
{
find: 'bootstrap/scss/card',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/card',
},
{
find: 'bootstrap/scss/carousel',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/carousel',
},
{
find: 'bootstrap/scss/close',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/close',
},
{
find: 'bootstrap/scss/code',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/code',
},
{
find: 'bootstrap/scss/custom-forms',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/custom-forms',
},
{
find: 'bootstrap/scss/dropdown',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/dropdown',
},
{
find: 'bootstrap/scss/forms',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/forms',
},
{
find: 'bootstrap/scss/functions',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/functions',
},
{
find: 'bootstrap/scss/grid',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/grid',
},
{
find: 'bootstrap/scss/images',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/images',
},
{
find: 'bootstrap/scss/input-group',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/input-group',
},
{
find: 'bootstrap/scss/jumbotron',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/jumbotron',
},
{
find: 'bootstrap/scss/list-group',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/list-group',
},
{
find: 'bootstrap/scss/media',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/media',
},
{
find: 'bootstrap/scss/mixins',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/mixins',
},
{
find: 'bootstrap/scss/modal',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/modal',
},
{
find: 'bootstrap/scss/nav',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/nav',
},
{
find: 'bootstrap/scss/navbar',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/navbar',
},
{
find: 'bootstrap/scss/pagination',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/pagination',
},
{
find: 'bootstrap/scss/popover',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/popover',
},
{
find: 'bootstrap/scss/print',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/print',
},
{
find: 'bootstrap/scss/progress',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/progress',
},
{
find: 'bootstrap/scss/reboot',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/reboot',
},
{
find: 'bootstrap/scss/root',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/root',
},
{
find: 'bootstrap/scss/tables',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/tables',
},
{
find: 'bootstrap/scss/toasts',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/toasts',
},
{
find: 'bootstrap/scss/tooltip',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/tooltip',
},
{
find: 'bootstrap/scss/transitions',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/transitions',
},
{
find: 'bootstrap/scss/type',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/type',
},
{
find: 'bootstrap/scss/utilities',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/utilities',
},
{
find: 'bootstrap/scss/variables',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/variables',
},
{
find: 'bootstrap/scss/spinners',
replaceWith: '@spartacus/styles/vendor/bootstrap/scss/spinners',
},
];

export function replaceBootstrapImports(): Rule {
return (tree: Tree, context: SchematicContext) => {
context.logger.info(
'Scanning files for Bootstrap imports. ' +
'Imports will be updated to use `@spartacus/styles/vendor/bootstrap/scss/`.'
);

tree.visit((filePath) => {
if (filePath.endsWith('.scss')) {
const fileContent = tree.read(filePath)?.toString('utf-8');
if (fileContent) {
let updatedContent = fileContent;
let hasChanges = false;

bootstrapImportsToReplace.forEach(({ find, replaceWith }) => {
const regex = new RegExp(`@import\\s+['"]${find}['"];`, 'g');
if (regex.test(updatedContent)) {
updatedContent = updatedContent.replace(
regex,
`@import '${replaceWith}';`
);
hasChanges = true;
}
});

if (hasChanges) {
tree.overwrite(filePath, updatedContent);
context.logger.info(
`Updated imports of Bootstrap in file '${filePath}'`
);
}
}
}
});

context.logger.info('Bootstrap import replacement process completed.');
return tree;
};
}
Loading
Loading