Removed starterkits and remaining grunt scripts

Issue #2852156 by markcarver: Move "overrides" source files and generated CSS to separate project
Moved to: https://github.com/unicorn-fail/drupal-bootstrap-stylesSigned-off-by: markcarver's avatarMark Carver <mark.carver@me.com>
parent 36dde42e
module.exports = function (grunt, options) {
'use strict';
// Register the "clean-vendor-dirs" task.
grunt.registerTask('clean-vendor-dirs', 'Ensures vendor directories do not cause Drupal to segfault. Read more in: MAINTAINERS.md.', function () {
var glob = [
'node_modules/**/*.info',
'bower_components/**/*.info',
'lib/**/*.info'
];
var files = grunt.file.expand(glob);
if (files.length) {
files.forEach(function (file) {
grunt.log.ok('Removed ' + file);
grunt.file.delete(file, {force: true});
});
}
grunt.log.ok('Vendor directories clean!');
});
};
var pkg = require('../package');
module.exports = function (grunt) {
'use strict';
var dev = !!grunt.option('dev');
var path, cssPath, libraries, librariesCache, librariesPath, versions, latestVersion;
// Helper function for falling back to a Bootstrap variables file.
var resolveVariables = function (file, backup) {
if (backup === true) grunt.verbose.write('Checking for backup variables file...');
if (!grunt.file.exists(path.join(librariesPath, file))) {
if (backup === true) grunt.verbose.warn();
grunt.verbose.writeln("Missing " + file);
file = false;
if (backup && backup !== true) {
file = resolveVariables(backup, true);
if (file) grunt.verbose.writeln("Using: " + file);
}
return file;
}
else if (backup === true) grunt.verbose.ok();
return file;
};
// Register the "compile" task.
grunt.registerTask('compile', 'Compiles the Drupal/Bootstrap override CSS files for the base theme.', function () {
var cwd = process.cwd();
path = require('path');
cssPath = path.join(cwd, pkg.paths.css);
librariesCache = path.join(cwd, pkg.caches.libraries);
librariesPath = path.join(cwd, pkg.paths.libraries);
if (!grunt.file.exists(librariesCache) || !grunt.file.isDir(librariesPath)) {
return grunt.fail.fatal('No libraries detected. Please run `grunt sync`.');
}
libraries = grunt.file.readJSON(librariesCache);
if (!libraries.bootstrap || !libraries.bootswatch) {
return grunt.fail.fatal('Invalid libraries cache. Please run `grunt sync`.');
}
versions = Object.keys(libraries.bootstrap);
latestVersion = [].concat(versions).pop();
grunt.config.set('latestVersion', latestVersion);
// Register a private sub-task. Doing this inside the main task prevents
// this private sub-task from being executed directly and also prevents it
// from showing up on the list of available tasks on --help.
grunt.registerTask('compile:overrides', function () {
var done = this.async();
var total = {count: 0};
var less = require('less');
var LessPluginAutoPrefix = require('less-plugin-autoprefix');
var LessPluginCleanCSS = require('less-plugin-clean-css');
var lessPlugins = [
new LessPluginCleanCSS({
advanced: true
}),
new LessPluginAutoPrefix({
browsers: [
"Android 2.3",
"Android >= 4",
"Chrome >= 20",
"Firefox >= 24",
"Explorer >= 8",
"iOS >= 6",
"Opera >= 12",
"Safari >= 6"
],
map: true
})
];
var queue = require('queue')({concurrency: 1, timeout: 60000});
// Iterate over libraries.
for (var library in libraries) {
if (!libraries.hasOwnProperty(library) || (dev && library !== 'bootstrap')) continue;
// Iterate over versions.
for (var version in libraries[library]) {
if (!libraries[library].hasOwnProperty(version) || (dev && version !== latestVersion)) continue;
// Iterate over themes.
for (var i = 0; i < libraries[library][version].length; i++) {
// Queue LESS compilations.
(function (library, versions, version, theme, total) {
queue.push(function (done) {
var lessPaths = [path.join(librariesPath)];
var latestVersion = [].concat(versions).pop();
var latestVariables = path.join(latestVersion, 'bootstrap', 'less', 'variables.less');
var latestMixins = path.join(latestVersion, 'bootstrap', 'less', 'mixins.less');
var themeVariables = path.join(version, library, (library === 'bootstrap' ? 'less' : theme), 'variables.less');
var backupVariables = path.join(version, 'bootstrap', 'less', 'variables.less');
var fileName = (library === 'bootstrap' ? 'overrides.min.css' : 'overrides-' + theme + '.min.css');
var outputFile = path.join(cssPath, version, fileName);
// Resolve the variable files.
latestVariables = resolveVariables(latestVariables);
if (!latestVariables) return done(false);
themeVariables = resolveVariables(themeVariables, backupVariables);
if (!themeVariables) return grunt.fail.fatal("Unable to create: " + outputFile);
var options = {
filename: outputFile,
paths: lessPaths,
plugins: lessPlugins
};
var imports = [
// First, import the latest bootstrap (missing variables).
'@import "' + latestVariables + '"',
// Then, override variables with theme.
'@import "' + themeVariables + '"',
// Then, import latest bootstrap mixins.
'@import "' + latestMixins + '"',
// Then, import the variable overrides.
'@import "' + path.join('starterkits', 'less', 'less', 'variable-overrides.less') + '"',
// Finally, import the base-theme overrides.
'@import "' + path.join('starterkits', 'less', 'less', 'overrides.less') + '"',
// Add some default variables that may not be available.
'@form-group-margin-bottom: 15px',
'@screen-sm-min: @screen-sm',
'@screen-md-min: @screen-md',
'@screen-lg-min: @screen-lg',
'@container-sm: @container-tablet',
'@container-md: @container-desktop',
'@container-large-desktop: (1140px + @grid-gutter-width)',
'@container-lg: @container-large-desktop'
];
grunt.log.debug("\noptions: " + JSON.stringify(options, null, 2));
grunt.log.debug(imports.join("\n"));
less.render(imports.join(';') + ';', options)
.then(function (output) {
total.count++;
grunt.log.writeln('Compiled '.green + path.join(version, fileName).white.bold);
grunt.file.write(outputFile, output.css);
done();
}, function (e) {
if (e.type === 'Parse') {
grunt.log.error("File:\t".red + e.filename.red);
grunt.log.error("Line:\t".red + [e.line, e.column].join(':').red);
grunt.log.error("Code:\t".red + e.extract.join('').white.bold);
grunt.log.writeln();
}
return grunt.fail.fatal(e);
})
;
});
})(library, Object.keys(libraries[library]), version, libraries[library][version][i], total);
}
}
}
// Start LESS compilations queues.
queue.start(function (e) {
// Report how many files were compiled.
grunt.log.ok(grunt.util.pluralize(total.count, '1 file compiled./' + total.count + ' files compiled.'));
return done(e);
});
});
// Run necessary sub-tasks.
var subtask = (dev ? 'dev' : 'css');
grunt.task.run([
'clean:' + subtask,
'compile:overrides',
'csslint:' + subtask
]);
});
};
module.exports = function (grunt, options) {
return {
options: {
browsers: [
"Android 2.3",
"Android >= 4",
"Chrome >= 20",
"Firefox >= 24",
"Explorer >= 8",
"iOS >= 6",
"Opera >= 12",
"Safari >= 6"
],
map: true
},
css: {
src: ['css/**/*.css']
},
dev: {
src: ['css/<%= latestVersion %>/overrides.css']
}
}
};
module.exports = function (grunt, options, arg1, arg2) {
return {
css: ['css/**/*.css'],
dev: ['css/<%= latestVersion %>/overrides.css']
};
};
module.exports = function () {
return {
options: {
csslintrc: '.csslintrc'
},
css: {
src: ['css/**/*.css']
},
dev: {
src: ['css/<%= latestVersion %>/overrides.min.css']
}
};
};
module.exports = function (grunt, options) {
return {
options: {
report: 'gzip'
},
css: {
files: [{
expand: true,
cwd: 'css',
src: ['**/*.css', '!**/*.min.css'],
dest: 'css',
ext: '.min.css'
}]
},
dev: {
files: [{
expand: true,
cwd: 'css',
src: ['<%= latestVersion %>/overrides.css'],
dest: 'css',
ext: '.min.css'
}]
}
}
}
module.exports = function (grunt, options) {
return {
install: {
options: {
template: '.githooks.js.hbs'
},
// Change to something else once the {{ hook }} variable can be used.
// @see https://github.com/wecodemore/grunt-githooks/pull/40
'pre-commit': 'pre-commit',
'post-merge': 'post-merge',
'post-checkout': 'post-checkout',
'post-commit': 'post-commit'
}
};
};
module.exports = function (grunt, options) {
return {
less: {
files: ['starterkits/less/**/*.less'],
tasks: ['compile']
}
}
}
module.exports = function (grunt) {
'use strict';
// Register the 'sync' task.
grunt.registerTask('sync', 'Syncs necessary libraries for development purposes. Read more in: MAINTAINERS.md.', function () {
var cwd = process.cwd();
var force = grunt.option('force');
var path = require('path');
var pkg = require('../package');
var mapSeries = require('promise-map-series');
var simpleJsonRequest = require('simple-json-request');
var semver = require('semver');
var getJson = function (uri) {
grunt.log.debug('Requesting JSON from: ' + uri);
return simpleJsonRequest.get({url: uri}).catch(function (e) {
grunt.log.fail('Unable to request JSON from:' + uri);
grunt.log.fail('(' + e.statusCode + ') ' + e);
return [];
});
};
// Internal variables.
var libraries = {};
var librariesCache = path.join(cwd, pkg.caches.libraries);
var librariesPath = path.join(cwd, pkg.paths.libraries);
var exists = grunt.file.exists(librariesCache);
var expired = false;
// Determine the validity of any existing cached libraries file.
if (!force && exists) {
grunt.verbose.write('Cached library information detected, checking...');
var fs = require('fs');
var stat = fs.statSync(librariesCache);
var now = new Date().getTime();
var expire = new Date(stat.mtime);
expire.setDate(expire.getDate() + 7); // 1 week
expired = now > expire.getTime();
grunt.verbose.writeln((expired ? 'EXPIRED' : 'VALID')[expired ? 'red' : 'green']);
}
// While the Bootswatch project attempts to maintain version parity with
// Bootstrap, it doesn't always happen. This causes issues when the system
// expects a 1:1 version match between Bootstrap and Bootswatch.
// @see https://github.com/thomaspark/bootswatch/issues/892#ref-issue-410070082
var mapVersion = function ($version, $package) {
if ($package === 'bootswatch') {
switch ($version) {
// This version is "broken" because of jsDelivr's API limit.
case '3.4.1':
$version = '3.4.0';
break;
// This version doesn't exist.
case '3.1.1':
$version = '3.2.0';
break;
}
}
return $version;
};
var getApiV1Json = function ($package) {
var $json = {name: $package, assets: []};
var $latest = '0.0.0';
var $versions = [];
grunt.log.writeln('Retrieving data for: ' + $package);
return getJson('https://data.jsdelivr.com/v1/package/npm/' + $package)
.catch(function (error) {
if (!(error instanceof Error)) {
error = new Error(error);
}
grunt.verbose.error(error);
})
.then(function ($packageJson) {
if (!$packageJson) {
$packageJson = {versions: []};
}
if ($packageJson.versions === void 0) {
$packageJson.versions = [];
}
return mapSeries($packageJson.versions, function ($version, $key) {
// Skip irrelevant versions.
if (!$version.match(/^3\.\d+\.\d+$/)) {
return Promise.resolve();
}
return getJson('https://data.jsdelivr.com/v1/package/npm/' + $package + '@' + mapVersion($version, $package) + '/flat')
.then(function ($versionJson) {
// Skip empty files.
if (!$versionJson.files || !$versionJson.files.length) {
return;
}
$versions.push($version);
if (semver.compare($latest, $version) === -1) {
$latest = $version;
}
var $asset = {files: [], version: $version};
$versionJson.files.forEach(function ($file) {
// Skip old bootswatch file structure.
if ($package === 'bootswatch' && $file.name.match(/^\/2|\/bower_components/)) {
return;
}
var $matches = $file.name.match(/([^/]*)\/bootstrap(-theme)?(\.min)?\.(js|css)$/, $file['name']);
if ($matches && $matches[1] !== void 0 && $matches[4] !== void 0 && $matches[1] !== 'custom') {
$asset.files.push($file.name);
}
});
$json.assets.push($asset);
$json.lastversion = $latest;
$json.versions = $versions;
})
});
})
.then(function () {
return $json;
});
};
// Register a private sub-task. Doing this inside the main task prevents
// this private sub-task from being executed directly and also prevents it
// from showing up on the list of available tasks on --help.
grunt.registerTask('sync:api', function () {
var done = this.async();
mapSeries(['bootstrap', 'bootswatch'], function ($package) {
return getApiV1Json($package);
}).then(function (json) {
grunt.verbose.write("\nExtracting versions and themes from libraries...");
libraries = {};
json.forEach(function (library) {
if (library.name === 'bootstrap' || library.name === 'bootswatch') {
library.assets.forEach(function (asset) {
if (asset.version.match(/^3.\d\.\d$/)) {
if (!libraries[library.name]) libraries[library.name] = {};
if (!libraries[library.name][asset.version]) libraries[library.name][asset.version] = {};
asset.files.forEach(function (file) {
if (!file.match(/bootstrap\.min\.css$/)) return;
if (library.name === 'bootstrap') {
libraries[library.name][asset.version]['bootstrap'] = true;
}
else {
libraries[library.name][asset.version][file.split(path.sep).filter(Boolean)[0]] = true;
}
});
}
});
}
});
grunt.verbose.ok();
// Flatten themes.
for (var library in libraries) {
grunt.verbose.header(library);
if (!libraries.hasOwnProperty(library)) continue;
var versions = Object.keys(libraries[library]);
grunt.verbose.ok('Versions: ' + versions.join(', '));
var themeCount = 0;
for (var version in libraries[library]) {
if (!libraries[library].hasOwnProperty(version)) continue;
var themes = Object.keys(libraries[library][version]).sort();
libraries[library][version] = themes;
if (themes.length > themeCount) {
themeCount = themes.length;
}
}
grunt.verbose.ok(grunt.util.pluralize(themeCount, 'Themes: 1/Themes: ' + themeCount));
}
grunt.verbose.writeln();
grunt.file.write(librariesCache, JSON.stringify(libraries, null, 2));
grunt.log.ok('Synced');
done();
});
});
// Run API sync sub-task.
if (!exists || force || expired) {
if (!exists) grunt.verbose.writeln('No libraries cache detected, syncing libraries.');
else if (force) grunt.verbose.writeln('Forced option detected, syncing libraries.');
else if (expired) grunt.verbose.writeln('Libraries cache is over a week old, syncing libraries.');
grunt.task.run(['sync:api']);
}
// Register another private sub-task.
grunt.registerTask('sync:libraries', function () {
var bower = require('bower');
var done = this.async();
var inquirer = require('inquirer');
var queue = require('queue')({concurrency: 1, timeout: 60000});
if (!grunt.file.exists(librariesCache)) {
return grunt.fail.fatal('No libraries detected. Please run `grunt sync --force`.');
}
libraries = grunt.file.readJSON(librariesCache);
var bowerJson = path.join(cwd, 'bower.json');
if (!grunt.file.isDir(librariesPath)) grunt.file.mkdir(librariesPath);
// Iterate over libraries.
for (var library in libraries) {
if (!libraries.hasOwnProperty(library)) continue;
// Iterate over versions.
for (var version in libraries[library]) {
if (!libraries[library].hasOwnProperty(version)) continue;
var endpoint = library + '#' + mapVersion(version, library);
// Check if library is already installed. If so, skip.
var versionPath = path.join(librariesPath, version);
grunt.verbose.write('Checking ' + endpoint + '...');
if (grunt.file.isDir(versionPath) && grunt.file.isDir(versionPath + '/' + library)) {
grunt.verbose.writeln('INSTALLED'.green);
continue;
}
grunt.verbose.writeln('MISSING'.red);
grunt.file.mkdir(versionPath);
grunt.file.copy(bowerJson, path.join(versionPath, 'bower.json'));
var config = {
cwd: versionPath,
directory: '',
interactive: true,
scripts: {
postinstall: 'rm -rf jquery && rm -rf font-awesome'
}
};
// Enqueue bower installations.
(function (endpoint, config) {
queue.push(function (done) {
bower.commands
.install([endpoint], {saveDev: true}, config)
.on('log', function (result) {
if (result.level === 'action' && result.id !== 'validate' && !result.message.match(/(jquery|font-awesome)/)) {
grunt.log.writeln(['bower', result.id.cyan, result.message.green].join(' '));
}
else if (result.level === 'action') {
grunt.verbose.writeln(['bower', result.id.cyan, result.message.green].join(' '));
}
else {
grunt.log.debug(['bower', result.id.cyan, result.message.green].join(' '));
}
})
.on('prompt', function (prompts, callback) {
inquirer.prompt(prompts, callback);
})
.on('end', function () { done() })
;
});
})(endpoint, config);
}
}
// Start bower queue.
queue.start(function (e) {
return done(e);
});
});
// Run private sub-task.
grunt.task.run(['sync:libraries']);
});
}
<!-- @file Instructions for subtheming using the CDN Starterkit. -->
<!-- @defgroup sub_theming_cdn -->
<!-- @ingroup sub_theming -->
# CDN Starterkit
The CDN Starterkit is rather simple to set up. You don't have to do anything
until you wish to override the default [Drupal Bootstrap] base theme settings
or provide additional custom CSS.
- [Prerequisite](#prerequisite)
- [Override Styles](#styles)
- [Override Settings](#settings)
- [Override Templates and Theme Functions](#registry)
## Prerequisite
Read the @link subtheme Sub-theming @endlink parent topic.
## Override Styles {#styles}
Open `./THEMENAME/css/style.css` and modify the file to your liking.
## Override Settings {#settings}
Please refer to the @link theme_settings Sub-theme Settings @endlink topic.
## Override Templates and Theme Functions {#registry}
Please refer to the @link registry Theme Registry @endlink topic.
[Drupal Bootstrap]: https://www.drupal.org/project/bootstrap
[Bootstrap Framework]: https://getbootstrap.com/docs/3.4/
[jsDelivr CDN]: http://www.jsdelivr.com
global-styling:
css:
theme:
css/style.css: {}
core: 8.x
type: theme
base theme: bootstrap
name: 'THEMETITLE'
description: 'Uses the jsDelivr CDN for all CSS and JavaScript. No source files or compiling is necessary and is recommended for simple sites or beginners.'
package: 'Bootstrap'
regions:
navigation: 'Navigation'
navigation_collapsible: 'Navigation (Collapsible)'
header: 'Top Bar'
highlighted: 'Highlighted'
help: 'Help'
content: 'Content'
sidebar_first: 'Primary'
sidebar_second: 'Secondary'
footer: 'Footer'
page_top: 'Page top'
page_bottom: 'Page bottom'
libraries:
- 'THEMENAME/global-styling'
<?php
/**
* @file
* Bootstrap sub-theme.
*
* Place your custom PHP code in this file.
*/
# Default settings should not be located here. This file is only used to
# initially install a theme's "settings".
#
# Instead, the Drupal Bootstrap base theme uses the @BootstrapSetting
# annotated plugin discovery process. This ensures that sub-theme settings are
# inherited from base themes themes properly and to determine when a setting
# should be saved to config as "overridden".
#
# @see \Drupal\bootstrap\Plugin\SettingBase
# @see \Drupal\bootstrap\Plugin\SettingInterface
# @see \Drupal\bootstrap\Plugin\SettingManager
# @see \Drupal\bootstrap\Plugin\Form\SystemThemeSettings
# @see \Drupal\bootstrap\ThemeSettings
# This file cannot be empty, so install an initial empty array for "schemas".
# @see https://www.drupal.org/node/2813829
schemas: []
# Schema for the theme setting configuration file of the THEMETITLE theme.
THEMENAME.settings:
type: theme_settings
label: 'THEMETITLE settings'
/**
* Place your custom styles here.
*/
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="32px" height="32px"
viewBox="0 0 32 32" preserveAspectRatio="none">
<g>
<image width="32" height="32" xlink:href="data:image/png;base64,
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ
bWFnZVJlYWR5ccllPAAAAohJREFUeNrEl1tIVGEQx12L0BQjoXwpoRfxIQUJuhAIvVRLIRQELYj1
oKDgpQgVNQhBChQfLFDLoovVS0RQYuqLPUgFgTcQia5CSBBERUiKuv0+GGGJPd/l7Nkc+DNn98w3
8/++mXNmTigajaasp4T8Ejhx+GIWavOTkbaviRBITWBtIbgDkfT1InAGHAGDkNj2X1NAwFLU3ZgN
fATnSMezpBIgcAaqWRBPHoFLEJlNVgpWRC943D8FJiDaA3IDOQEc5aNK5Ocou3vDf3u5HgFbNEu/
gzrs7/smQKAa1BWQEfN3L06ruFcmdWCSa+A8a1acUkCAetTVf4IrqeReBIf3pPhMojbR41QDBDiO
atc4LRU9ZVk7FXKaZgLyhus2OExbS6FDAV+OV5jxTqAa7DQ4e4qzTegiBwKZoFFLAKdqZ+UGR8Pk
vwu9A7i+AU8TY6vuBNTjtcvgJB0nJyGhCvAgeO9AIBsc0hHYb+GkGDyGxG1ITHIdlmfeVop1BAoc
HJ2FxENIqBOodVhXoCOw3TGnEfVCgsQDrmcc0uBJYNVHR20S3R/EPDDvw0c+p6AK94Wl/aKOwLTP
uSIPfAHLFrYfdAReJzBZ2abvpY7ABHjrg8AcyAEbDXZL4LknAapZGdxyDD4vpPdZ2A7JC0zbC3pl
R7bSJ70+YjFNtVoNJNKObQbMcXAA7AFjhhGvA6INVvMAhgOoBkNwNQsck5Z8wxB8wGuQ9VwEiQ7U
BSmcWPkGOiXnv2QS3q0Jru6rCWrZ71BaKH0/JLXxCmd/+F8RuKkJ/gO0YNsd6IcJgcPSfI56mLxT
3RJcJ/jnoD9MNsg3oZobcqWxqGf/J/gkdTFJ4N9J/zoOSv4KMAC++teHNUg1rQAAAABJRU5ErkJg
gg==
"/>
</g>
</svg>