Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ insert_final_newline = true
[*.yml]
indent_style = space
indent_size = 2

# Vendored files - preserve upstream formatting
[lib/external/**]
indent_style = unset
trim_trailing_whitespace = unset
insert_final_newline = unset
19 changes: 15 additions & 4 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,37 @@ on:
branches-ignore: "dependabot/**"

jobs:
build:
build-and-test:
# Update it to `ubuntu-latest` only when:
# - our infra is on Debian >=13 which has ImageMagick 7
# - `ubuntu-latest` resolves to v26.x which has ImageMagick 7
# When doing that, also update the `convert` command to `magick`.
runs-on: ubuntu-24.04
name: ${{ matrix.NAME }} (${{ matrix.NODE_VERSION }})
strategy:
fail-fast: false
matrix:
NAME: [ "test" ]
NPM_SCRIPT: [ "qunit" ]

# Node.js 18 is required by jqueryui.com
# Remember to include the version used in Dockerfile!
NODE_VERSION: [18.x, 20.x, 22.x, 24.x]
include:
- NAME: "lint"
NODE_VERSION: "24.x"
NPM_SCRIPT: "lint"
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Update apt-get cache
run: sudo apt-get update
if: "!contains(matrix.NPM_SCRIPT, 'lint')"

- name: Install xsltproc & ImageMagick 6
run: sudo apt-get install -y xsltproc imagemagick
if: "!contains(matrix.NPM_SCRIPT, 'lint')"

- name: Use Node.js ${{ matrix.NODE_VERSION }}
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
Expand All @@ -38,8 +48,9 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Prepare for tests
run: ./node_modules/.bin/grunt prepare
- name: Build
run: npm run build
if: "!contains(matrix.NPM_SCRIPT, 'lint')"

- name: Run tests
run: npm test
run: npm run ${{ matrix.NPM_SCRIPT }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.eslintcache
app/dist
jquery-ui
log/downloads.log
Expand Down
8 changes: 7 additions & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ grunt.initConfig( {
}
},
eslint: {
all: [ "*.js", "test/*js", "lib/**/*.js", "app/src/*.js" ]
all: [
"*.js",
"test/*js",
"lib/**/*.js",
"!lib/external/**/*.js",
"app/src/*.js"
]
},
copy: {
appExternalFarbtastic: {
Expand Down
1 change: 0 additions & 1 deletion app/src/download.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-env jquery, browser */
/*global Hash: false, JST: false, Model: false */
/*!
* jQuery UI DownloadBuilder client-side JavaScript file
Expand Down
1 change: 0 additions & 1 deletion app/src/hash.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-env jquery, browser */
/*global EventEmitter: false */
/*!
* jQuery UI helper JavaScript file for History and hash support
Expand Down
1 change: 0 additions & 1 deletion app/src/model.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-env jquery, browser */
/*global EventEmitter: false, LZMA: false, QueryString: false */
/*!
* jQuery UI helper JavaScript file for DownloadBuilder and ThemeRoller models
Expand Down
1 change: 0 additions & 1 deletion app/src/querystring.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-env jquery, browser */
/*!
* jQuery UI helper JavaScript file for QueryString support
*
Expand Down
1 change: 0 additions & 1 deletion app/src/themeroller.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-env jquery, browser */
/*global Hash: false, JST: false, Model: false, QueryString: false */
/*!
* jQuery UI ThemeRoller client-side JavaScript file
Expand Down
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default [
"app/dist/**",
"external/**",
"jquery-ui/**",
"lib/external/**",
"template/**",
"test/fixtures/**",
"tmp/**",
Expand Down
112 changes: 112 additions & 0 deletions lib/builder-amd-css.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* Generates the CSS bundle of an AMD modular project that uses the `css!`
* plugin definitions. It's not a substitute for css plugins like require-css;
* it uses require-css (vendored in lib/external/require-css) internally.
*/

"use strict";

var requireCssFiles,
fs = require( "node:fs" ),
path = require( "node:path" ),
requirejs = require( "./requirejs-memfiles" );

var requireCssDir = __dirname + "/external/require-css";

requireCssFiles = {
"require-css/css-builder.js": fs.readFileSync( requireCssDir + "/css-builder.js" ),
"require-css/css.js": fs.readFileSync( requireCssDir + "/css.js" ),
"require-css/normalize.js": fs.readFileSync( requireCssDir + "/normalize.js" )
};

function buildCss( files, config, callback ) {
var include;

if ( typeof config !== "object" ) {
return callback( new Error( "missing or invalid config (object expected)" ) );
}
if ( !Array.isArray( config.include ) ) {
return callback( new Error( "missing or invalid config.include (array expected)" ) );
}

include = config.include;
delete config.include;

// Include require-css files.
Object.keys( requireCssFiles ).forEach( function( filepath ) {
files[ filepath ] = requireCssFiles[ filepath ];
} );

function normalizePath( _path ) {
return path.normalize( _path ).replace( /^\//, "" );
}

config = Object.assign( {}, config );
config.appDir = config.appDir || ".";
config.baseUrl = config.baseUrl || ".";
config.paths = config.paths || {};
config.paths[ "require-css" ] = path.relative( config.appDir, "require-css" );
config.map = config.map || {};
config.map[ "*" ] = config.map[ "*" ] || {};
config.map[ "*" ].css = "require-css/css";
config.asReference = {
saveFile: function( path, data ) {
path = normalizePath( path );
files[ path ] = data;
},
loadFile: function( path ) {
var data;
path = normalizePath( path );
data = files[ path ];
if ( config.onCssBuildWrite ) {
data = config.onCssBuildWrite( path, data );
}
return data;
}
};
config.optimizeCss = "none";
config.separateCSS = true;
config = Object.assign( config, {
dir: "dist",
modules: [ {
name: "output",
include: include,
create: true
} ]
} );

requirejs.setFiles( files, function( done ) {
requirejs.optimize( config, function() {
callback( null, files[ "dist/output.css" ] || "", files );
done();
}, function( error ) {
callback( error );
done();
} );
} );
}

/**
* @param {Object} files Object containing (path, data) key-value pairs.
* @param {Object} config require.js build configuration.
* @param {Function} callback Function( error, builtCss, files )
*/
module.exports = function( files, config, callback ) {
var clonedFiles = {};

try {

// Clone files + make sure all CSSes are String utf-8.
Object.keys( files ).forEach( function( path ) {
if ( /\.css$/i.test( path ) ) {
clonedFiles[ path ] = files[ path ].toString( "utf-8" );
} else {
clonedFiles[ path ] = files[ path ];
}
} );

buildCss( clonedFiles, config, callback );
} catch ( error ) {
callback( error );
}
};
67 changes: 67 additions & 0 deletions lib/builder-amd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Generates the JS bundle of an AMD modular project.
* Ideal for applications that build bundles on the fly using Node.js.
*/

"use strict";

var requirejs = require( "./requirejs-memfiles" ),
util = require( "util" );

function buildJs( files, config, callback ) {
var exclude, include;

if ( typeof config !== "object" ) {
return callback( new Error( "missing or invalid config (object expected)" ) );
}
if ( !Array.isArray( config.include ) ) {
return callback( new Error( "missing or invalid config.include (array expected)" ) );
}

exclude = config.exclude;
include = config.include;
delete config.exclude;
delete config.include;

config = util._extend( {}, config );
config.appDir = config.appDir || ".";
config.baseUrl = config.baseUrl || ".";
config = util._extend( config, {
dir: "dist",
modules: [ {
name: "output",
include: include,
create: true
} ]
} );

if ( exclude ) {
config.modules[ 0 ].exclude = exclude;
}

requirejs.setFiles( files, function( done ) {
requirejs.optimize( config, function() {
callback( null, files[ "dist/output.js" ], files );
done();
}, function( error ) {
callback( error );
done();
} );
} );
}

/**
* @param {Object} files Object containing (path, data) key-value pairs.
* @param {Object} config require.js build configuration.
* @param {Function} callback Function( error, builtJs, files )
*/
module.exports = function( files, config, callback ) {
var clonedFiles = {};

// Clone files
Object.keys( files ).forEach( function( path ) {
clonedFiles[ path ] = files[ path ];
} );

buildJs( clonedFiles, config, callback );
};
101 changes: 101 additions & 0 deletions lib/builder-jquery-css.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* Generates the CSS bundle of a jQuery project that uses JS comments like
* `//>> css.<name>: files` to define its CSS dependencies and that uses AMD
* definitions to define its JS dependencies.
*/

"use strict";

var amdCssBuilder = require( "./builder-amd-css" );

function cssDependencies( data, which ) {
var result = [],
regexp = new RegExp( "\\/\\/>>\\s*css\\." + which + ":(.*)", "g" );

data.replace( regexp, function( garbage, input ) {
input = input.split( "," ).map( trim );
result.push.apply( result, input );
} );

return result.map( function( cssDependency ) {
return "\"css!" + cssDependency.replace( /\.css$/i, "" ) + "\"";
} );
}

function jsDependencies( data ) {
var match = data.match( /define\(\ ?\[([^\]]*?)\]/ );
if ( match === null ) {
return [];
}
return match[ 1 ].replace( /\/\/.+/g, "" ).split( "," ).map( trim );
}

/**
* transform( data, which )
*
* @data [String] File content.
*
* @which [String] The name of the css bundle selector.
*
* Parse the syntax (a) and transform it into the AMD definition (b).
*
* a: //>> css.<which>: cssFile1, cssFile2, ...
* define([ "foo", "bar" ], function() { ... });
*
* b: define([ "css!cssFile1", "css!cssFile2", "css!...", "foo", "bar" ]);
*/
function transform( data, which ) {
var dependencies = [];

dependencies.push.apply( dependencies, cssDependencies( data, which ) );
dependencies.push.apply( dependencies, jsDependencies( data ) );

return "define([" + dependencies.join( ", " ) + "]);";
}

// Helper: trim.
function trim( string ) {
return string.trim();
}

/**
* transformFiles( files, which )
*
* @files [Object]
*
* @which [String] The name of the css bundle selector.
*
* Transform the content of each file according to `transform()`.
* The original files Object is preserved intact.
*/
function transformFiles( files, which ) {
var transformedFiles = {};
Object.keys( files ).forEach( function( path ) {
var data;
if ( /\.js$/.test( path ) ) {
data = files[ path ].toString( "utf-8" );
transformedFiles[ path ] = transform( data, which );
} else if ( /\.css$/.test( path ) ) {
transformedFiles[ path ] = files[ path ].toString( "utf-8" );
} else {
transformedFiles[ path ] = files[ path ];
}
} );
return transformedFiles;
}


/**
* @param {Object} files Object containing (path, data) key-value pairs.
* @param {string} which CSS name selector (e.g., "structure", "theme").
* @param {Object} config require.js build configuration.
* @param {Function} callback Function( error, builtCss, files )
*/
module.exports = function( files, which, config, callback ) {
try {
var transformedFiles = transformFiles( files, which );
amdCssBuilder( transformedFiles, config, callback );
} catch ( error ) {
callback( error );
}
};
Loading