@ng-bootstrap/ng-bootstrap
Version:
Angular powered Bootstrap
220 lines (210 loc) • 8.94 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var schematics = require('@angular-devkit/schematics');
var path = require('path');
var messages = require('../messages-Coq8X1sU.js');
var core = require('@angular-devkit/core');
var utility = require('@schematics/angular/utility');
var ngAstUtils = require('@schematics/angular/utility/ng-ast-utils');
var astUtils = require('@schematics/angular/utility/ast-utils');
var change = require('@schematics/angular/utility/change');
var ts = require('@schematics/angular/third_party/github.com/Microsoft/TypeScript/lib/typescript');
var util = require('@schematics/angular/utility/standalone/util');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
var ts__namespace = /*#__PURE__*/_interopNamespaceDefault(ts);
// Regular expression that matches all possible Angular CLI default style files
const defaultStyleFileRegex = /styles\.(c|le|sc|sa)ss/;
// Regular expression that matches all files that have a proper stylesheet extension
const validStyleFileRegex = /\.(c|le|sc|sa)ss/;
/**
* Resolves options for the build target of the given project
*/
function getProjectTargetOptions(project, buildTarget) {
const buildTargetObject = project.targets.get(buildTarget);
if (buildTargetObject && buildTargetObject.options) {
return buildTargetObject.options;
}
throw new schematics.SchematicsException(`Cannot determine project target configuration for: ${buildTarget}.`);
}
/**
* Gets a style file with the given extension in a project and returns its path. If no
* extension is specified, any style file with a valid extension will be returned.
*/
function getProjectStyleFile(project, extension) {
const buildOptions = getProjectTargetOptions(project, 'build');
if (buildOptions.styles && Array.isArray(buildOptions.styles) && buildOptions.styles.length) {
const styles = buildOptions.styles.map((s) => (typeof s === 'string' ? s : s['input']));
// Look for the default style file that is generated for new projects by the Angular CLI. This
// default style file is usually called `styles.ext` unless it has been changed explicitly.
const defaultMainStylePath = styles.find((file) => defaultStyleFileRegex.test(file));
if (defaultMainStylePath) {
return core.normalize(defaultMainStylePath);
}
// If no default style file could be found, use the first style file that matches the given
// extension. If no extension specified explicitly, we look for any file with a valid style
// file extension.
const fallbackStylePath = styles.find((file) => validStyleFileRegex.test(file));
if (fallbackStylePath) {
return core.normalize(fallbackStylePath);
}
}
return null;
}
const BOOTSTRAP_CSS_FILEPATH = 'node_modules/bootstrap/dist/css/bootstrap.min.css';
const SUPPORTED_BOOTSTRAP_STYLE_IMPORTS = {
'.sass': `
/* Importing Bootstrap SCSS file. */
@import 'bootstrap/scss/bootstrap'
`,
'.scss': `
/* Importing Bootstrap SCSS file. */
@import 'bootstrap/scss/bootstrap';
`,
};
/**
* Adding bootstrap either to 'styles.scss' or 'styles.sass'
* If not possible, we're simply adding 'bootstrap.css' to the 'angular.json'
*/
function addBootstrapStyles(options) {
return async (host, context) => {
const workspace = await utility.readWorkspace(host);
const projectName = options.project || workspace.extensions.defaultProject.toString();
const project = workspace.projects.get(projectName);
if (!project) {
throw new schematics.SchematicsException(messages.noProject(projectName));
}
const styleFilePath = getProjectStyleFile(project) || '';
const styleFileExtension = path__namespace.extname(styleFilePath);
const styleFilePatch = SUPPORTED_BOOTSTRAP_STYLE_IMPORTS[styleFileExtension];
// found supported styles
if (styleFilePatch) {
return addBootstrapToStylesFile(styleFilePath, styleFilePatch);
}
else {
// found some styles, but unsupported
if (styleFileExtension !== '.css' && styleFileExtension !== '') {
context.logger.warn(messages.unsupportedStyles(styleFilePath));
}
// just patching 'angular.json'
addBootstrapToAngularJson(project);
await utility.writeWorkspace(host, workspace);
}
};
}
/**
* Patches 'styles.scss' or 'styles.sass' to add Bootstrap snippet
*/
function addBootstrapToStylesFile(styleFilePath, styleFilePatch) {
return (host) => {
const styleContent = host.read(styleFilePath).toString('utf-8');
const recorder = host.beginUpdate(styleFilePath);
recorder.insertRight(styleContent.length, styleFilePatch);
host.commitUpdate(recorder);
};
}
/**
* Patches 'angular.json' to add 'bootstrap.css' styles
*/
function addBootstrapToAngularJson(project) {
const targetOptions = getProjectTargetOptions(project, 'build');
const styles = targetOptions.styles;
if (!styles) {
targetOptions.styles = [BOOTSTRAP_CSS_FILEPATH];
}
else {
const existingStyles = styles.map((s) => (typeof s === 'string' ? s : s['input']));
for (const [, stylePath] of existingStyles.entries()) {
// If the given asset is already specified in the styles, we don't need to do anything.
if (stylePath === BOOTSTRAP_CSS_FILEPATH) {
return;
}
}
styles.unshift(BOOTSTRAP_CSS_FILEPATH);
}
}
const NG_BOOTSTRAP_MODULE_NAME = 'NgbModule';
const NG_BOOTSTRAP_PACKAGE_NAME = '@ng-bootstrap/ng-bootstrap';
/**
* Patches main application module by adding 'NgbModule' import.
*
* Relevant 'angular.json' structure is:
*
* {
* "projects" : {
* "projectName": {
* "architect": {
* "build": {
* "options": {
* "main": "src/main.ts"
* }
* }
* }
* }
* },
* "defaultProject": "projectName"
* }
*
*/
function addNgbModuleToAppModule(options) {
return async (host) => {
const workspace = await utility.readWorkspace(host);
const projectName = options.project || workspace.extensions.defaultProject;
// 1. getting project by name
const project = workspace.projects.get(projectName);
if (!project) {
throw new schematics.SchematicsException(messages.noProject(projectName));
}
// 2. getting main file for project
const mainFilePath = await util.getMainFilePath(host, projectName);
if (!mainFilePath || !host.read(mainFilePath)) {
throw new schematics.SchematicsException(messages.noMainFile(projectName));
}
// we're not patching standalone apps
if (!ngAstUtils.isStandaloneApp(host, mainFilePath)) {
// 3. getting main app module file
const appModuleFilePath = ngAstUtils.getAppModulePath(host, mainFilePath);
const appModuleFileText = host.read(appModuleFilePath);
if (appModuleFileText === null) {
throw new schematics.SchematicsException(messages.noModuleFile(appModuleFilePath));
}
// 4. adding `NgbModule` to the app module
const appModuleSource = ts__namespace.createSourceFile(appModuleFilePath, appModuleFileText.toString('utf-8'), ts__namespace.ScriptTarget.Latest, true);
const changes = astUtils.addImportToModule(appModuleSource, appModuleFilePath, NG_BOOTSTRAP_MODULE_NAME, NG_BOOTSTRAP_PACKAGE_NAME);
const recorder = host.beginUpdate(appModuleFilePath);
for (const change$1 of changes) {
if (change$1 instanceof change.InsertChange) {
recorder.insertLeft(change$1.pos, change$1.toAdd);
}
}
host.commitUpdate(recorder);
}
};
}
/**
* Sets up a project with all required to run ng-bootstrap.
* This is run after 'package.json' was patched and all dependencies installed
*/
function ngAddSetupProject(options) {
return schematics.chain([
addNgbModuleToAppModule(options),
addBootstrapStyles(options),
schematics.externalSchematic('@angular/localize', 'ng-add', options.project ? { project: options.project } : {}),
]);
}
exports.default = ngAddSetupProject;