Files
bootswatch/Gruntfile.js
2025-05-11 11:56:28 -04:00

296 lines
6.7 KiB
JavaScript

'use strict';
const path = require('path');
const sass = require('sass');
const autoprefixer = require('autoprefixer');
const pkg = require('./package.json');
const SWATCHES = [
'brite',
'cerulean',
'cosmo',
'cyborg',
'darkly',
'flatly',
'journal',
'litera',
'lumen',
'lux',
'materia',
'minty',
'morph',
'pulse',
'quartz',
'sandstone',
'simplex',
'sketchy',
'slate',
'solar',
'spacelab',
'superhero',
'united',
'vapor',
'yeti',
'zephyr'
];
const BUILD_DIR = 'build/';
const DIST_DIR = 'dist/';
const DOCS_DEST = 'docs/5/';
let buildTheme = '';
module.exports = grunt => {
grunt.loadNpmTasks('@lodder/grunt-postcss');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-sass');
grunt.loadNpmTasks('grunt-html');
grunt.loadNpmTasks('grunt-shell');
// Force use of Unix newlines
grunt.util.linefeed = '\n';
grunt.initConfig({
clean: {
build: {
src: 'dist/*/build.scss'
}
},
concat: {
dist: {
src: [],
dest: ''
}
},
copy: {
vendor: {
files: [{
expand: true,
cwd: 'node_modules/bootstrap-icons',
src: ['font/**'],
dest: 'docs/_vendor/bootstrap-icons/'
}, {
expand: true,
cwd: 'node_modules/prismjs',
src: ['prism.js', 'themes/prism-okaidia.css'],
dest: 'docs/_vendor/prismjs/'
}, {
expand: true,
cwd: 'node_modules/bootstrap',
src: ['dist/**'],
dest: 'docs/_vendor/bootstrap/'
}]
},
css: {
files: [{
expand: true,
cwd: 'dist',
src: [
'**/*.css',
'**/*.scss',
'**/*.map'
],
dest: DOCS_DEST
}]
}
},
sass: {
options: {
implementation: sass,
outputStyle: 'expanded'
},
dist: {
src: [],
dest: ''
},
docs: {
options: {
outputStyle: 'compressed',
},
src: 'docs/_assets/scss/custom.scss',
dest: 'docs/_assets/css/custom.min.css'
}
},
postcss: {
options: {
processors: [
autoprefixer({ cascade: false })
]
},
dist: {
src: [],
dest: ''
},
docs: {
src: 'docs/_assets/css/custom.min.css',
dest: 'docs/_assets/css/custom.min.css'
}
},
cssmin: {
options: {
level: {
1: {
specialComments: 'all'
}
},
sourceMap: true
},
dist: {
src: [],
dest: ''
},
rtl: {
src: [],
dest: ''
}
},
htmllint: {
options: {
ignore: [
/Attribute “autocomplete” is only allowed when the input type is.*/
]
},
src: [
'docs/**/*.html',
'!docs/**/bower_components/**/*.html',
'!docs/_vendor/**/*.html',
'!docs/2/**/*.html'
]
},
connect: {
options: {
hostname: 'localhost',
livereload: 35729,
port: 3000,
open: true
},
base: {
options: {
base: 'docs'
}
}
},
watch: {
options: {
livereload: '<%= connect.options.livereload %>',
spawn: false
},
dev: {
files: [
'dist/*/_variables.scss',
'dist/*/_bootswatch.scss'
],
tasks: ['build', 'shell:csslint']
}
},
shell: {
options: {
stderr: false,
failOnError: false
},
rtlcss: {
command: function (theme) {
return `rtlcss dist/${theme}/bootstrap.css dist/${theme}/bootstrap.rtl.css`;
}
},
csslint: {
command: 'stylelint "dist/**/*.scss"'
}
}
});
grunt.registerTask('rtlcss', function (theme) {
grunt.task.run('shell:rtlcss:' + theme);
});
grunt.registerTask('build', 'build a regular theme from scss', theme => {
theme = theme ? theme : buildTheme;
const themeDir = path.join(DIST_DIR, '/', theme);
const isValidTheme = grunt.file.exists(path.join(themeDir, '/_variables.scss')) && grunt.file.exists(path.join(themeDir, '/_bootswatch.scss'));
// cancel the build (without failing) if this directory is not a valid theme
if (!isValidTheme) {
grunt.log.writeln(`${theme} theme does not exist!`);
return;
}
const concatSrc = path.join(BUILD_DIR, '/scss/build.scss');
const concatDest = path.join(themeDir, '/build.scss');
const cssDest = path.join(themeDir, '/bootstrap.css');
const cssDestMin = path.join(themeDir, '/bootstrap.min.css');
const cssDestRtl = path.join(themeDir, '/bootstrap.rtl.css');
const cssDestRtlMin = path.join(themeDir, '/bootstrap.rtl.min.css');
const banner = `/*!
* Bootswatch v${pkg.version} (${pkg.homepage})
* Theme: ${theme}
* Copyright 2012-${new Date().getFullYear()} ${pkg.author}
* Licensed under ${pkg.license}
* Based on Bootstrap
*/
`;
grunt.config.set('concat.options', {banner});
grunt.config.set('concat.dist', {
src: concatSrc,
dest: concatDest
});
grunt.config.set('sass.dist', {
src: concatDest,
dest: cssDest
});
grunt.config.set('postcss.dist', {
src: cssDest,
dest: cssDest
});
grunt.config.set('copy.css.files.0.cwd', themeDir);
grunt.config.set('copy.css.files.0.dest', path.join(DOCS_DEST, theme));
grunt.config.set('cssmin.dist', {
src: cssDest,
dest: cssDestMin
});
grunt.config.set('cssmin.rtl', {
src: cssDestRtl,
dest: cssDestRtlMin
});
grunt.task.run([
'concat',
'sass:dist',
'postcss:dist',
'clean:build',
'cssmin:dist',
`rtlcss:${theme}`,
'cssmin:rtl',
'copy:css'
]);
});
grunt.registerTask('swatch', 'build a theme from scss ', theme => {
// If no theme is passed, build all swatches
theme = theme ? [theme] : SWATCHES;
theme.forEach(t => {
grunt.task.run(`build:${t}`);
});
});
grunt.event.on('watch', (action, filepath) => {
const theme = path.basename(path.dirname(filepath));
grunt.log.writeln(`${theme} changed...`);
buildTheme = theme;
});
grunt.registerTask('default', ['connect', 'watch']);
grunt.registerTask('vendor', 'copy:vendor');
grunt.registerTask('docs-css', ['sass:docs', 'postcss:docs']);
grunt.registerTask('docs', ['docs-css', 'vendor']);
grunt.registerTask('release', ['swatch', 'docs']);
};