Build: replace CRLF with LF during minify
[jquery.git] / Gruntfile.cjs
blob51f36a8e44ac36f919aec5a5956bcea3dff420e1
1 "use strict";
3 module.exports = function( grunt ) {
4         function readOptionalJSON( filepath ) {
5                 const stripJSONComments = require( "strip-json-comments" );
6                 let data = {};
7                 try {
8                         data = JSON.parse( stripJSONComments(
9                                 fs.readFileSync( filepath, { encoding: "utf8" } )
10                         ) );
11                 } catch ( e ) {}
12                 return data;
13         }
15         const fs = require( "fs" );
16         const { spawn } = require( "child_process" );
17         const gzip = require( "gzip-js" );
18         const nodeV16OrNewer = !/^v1[0-5]\./.test( process.version );
19         const nodeV17OrNewer = !/^v1[0-6]\./.test( process.version );
20         const customBrowsers = process.env.BROWSERS && process.env.BROWSERS.split( "," );
22         // Support: Node.js <16
23         // Skip running tasks that dropped support for old Node.js in these Node versions.
24         function runIfNewNode( task ) {
25                 return nodeV16OrNewer ? task : "print_old_node_message:" + task;
26         }
28         if ( nodeV16OrNewer ) {
29                 const playwright = require( "playwright-webkit" );
30                 process.env.WEBKIT_HEADLESS_BIN = playwright.webkit.executablePath();
31         }
33         if ( !grunt.option( "filename" ) ) {
34                 grunt.option( "filename", "jquery.js" );
35         }
37         grunt.option( "dist-folder", grunt.option( "esm" ) ? "dist-module" : "dist" );
39         const builtJsFiles = [
40                 "dist/jquery.js",
41                 "dist/jquery.min.js",
42                 "dist/jquery.slim.js",
43                 "dist/jquery.slim.min.js",
44                 "dist-module/jquery.module.js",
45                 "dist-module/jquery.module.min.js",
46                 "dist-module/jquery.slim.module.js",
47                 "dist-module/jquery.slim.module.min.js"
48         ];
50         const builtJsMinFiles = builtJsFiles
51                 .filter( filepath => filepath.endsWith( ".min.js" ) );
53         grunt.initConfig( {
54                 pkg: grunt.file.readJSON( "package.json" ),
55                 dst: readOptionalJSON( "dist/.destination.json" ),
56                 compare_size: {
57                         files: builtJsMinFiles,
58                         options: {
59                                 compress: {
60                                         gz: function( contents ) {
61                                                 return gzip.zip( contents, {} ).length;
62                                         }
63                                 },
64                                 cache: "build/.sizecache.json"
65                         }
66                 },
67                 babel: {
68                         options: {
69                                 sourceMap: "inline",
70                                 retainLines: true,
71                                 plugins: [ "@babel/transform-for-of" ]
72                         },
73                         tests: {
74                                 files: {
75                                         "test/data/core/jquery-iterability-transpiled.js":
76                                                 "test/data/core/jquery-iterability-transpiled-es6.js"
77                                 }
78                         }
79                 },
80                 build: {
81                         all: {
82                                 dest: "dist/jquery.js",
83                                 minimum: [
84                                         "core"
85                                 ],
87                                 // Exclude specified modules if the module matching the key is removed
88                                 removeWith: {
89                                         ajax: [ "manipulation/_evalUrl", "deprecated/ajax-event-alias" ],
90                                         callbacks: [ "deferred" ],
91                                         css: [ "effects", "dimensions", "offset" ],
92                                         "css/showHide": [ "effects" ],
93                                         deferred: {
94                                                 remove: [ "ajax", "effects", "queue", "core/ready" ],
95                                                 include: [ "core/ready-no-deferred" ]
96                                         },
97                                         event: [ "deprecated/ajax-event-alias", "deprecated/event" ],
98                                         selector: [ "css/hiddenVisibleSelectors", "effects/animatedSelector" ]
99                                 }
100                         }
101                 },
102                 npmcopy: {
103                         all: {
104                                 options: {
105                                         destPrefix: "external"
106                                 },
107                                 files: {
108                                         "bootstrap/bootstrap.css": "bootstrap/dist/css/bootstrap.css",
109                                         "bootstrap/bootstrap.min.css": "bootstrap/dist/css/bootstrap.min.css",
110                                         "bootstrap/bootstrap.min.css.map": "bootstrap/dist/css/bootstrap.min.css.map",
112                                         "core-js-bundle/core-js-bundle.js": "core-js-bundle/minified.js",
113                                         "core-js-bundle/LICENSE": "core-js-bundle/LICENSE",
115                                         "npo/npo.js": "native-promise-only/lib/npo.src.js",
117                                         "qunit/qunit.js": "qunit/qunit/qunit.js",
118                                         "qunit/qunit.css": "qunit/qunit/qunit.css",
119                                         "qunit/LICENSE.txt": "qunit/LICENSE.txt",
121                                         "requirejs/require.js": "requirejs/require.js",
123                                         "sinon/sinon.js": "sinon/pkg/sinon.js",
124                                         "sinon/LICENSE.txt": "sinon/LICENSE"
125                                 }
126                         }
127                 },
128                 jsonlint: {
129                         pkg: {
130                                 src: [ "package.json" ]
131                         }
132                 },
133                 eslint: {
134                         options: {
135                                 maxWarnings: 0
136                         },
138                         // We have to explicitly declare "src" property otherwise "newer"
139                         // task wouldn't work properly :/
140                         dist: {
141                                 src: builtJsFiles
142                         },
143                         dev: {
144                                 src: [
145                                         "src/**/*.js",
146                                         "Gruntfile.js",
147                                         "test/**/*.js",
148                                         "build/**/*.js",
150                                         // Ignore files from .eslintignore
151                                         // See https://github.com/sindresorhus/grunt-eslint/issues/119
152                                         ...fs
153                                                 .readFileSync( `${ __dirname }/.eslintignore`, "utf-8" )
154                                                 .split( "\n" )
155                                                 .filter( filePath => filePath )
156                                                 .map( filePath => filePath[ 0 ] === "!" ?
157                                                         filePath.slice( 1 ) :
158                                                         `!${ filePath }`
159                                                 ),
161                                         // Explicitly ignore `dist/` & `dist-module/` as it could be unignored
162                                         // by the above `.eslintignore` parsing.
163                                         "!dist/**/*.js",
164                                         "!dist-module/**/*.js"
165                                 ]
166                         }
167                 },
168                 testswarm: {
169                         tests: [
171                                 // A special module with basic tests, meant for not fully
172                                 // supported environments like jsdom. We run it everywhere,
173                                 // though, to make sure tests are not broken.
174                                 "basic",
176                                 "ajax",
177                                 "animation",
178                                 "attributes",
179                                 "callbacks",
180                                 "core",
181                                 "css",
182                                 "data",
183                                 "deferred",
184                                 "deprecated",
185                                 "dimensions",
186                                 "effects",
187                                 "event",
188                                 "manipulation",
189                                 "offset",
190                                 "queue",
191                                 "selector",
192                                 "serialize",
193                                 "support",
194                                 "traversing",
195                                 "tween"
196                         ]
197                 },
198                 karma: {
199                         options: {
200                                 customContextFile: "test/karma.context.html",
201                                 customDebugFile: "test/karma.debug.html",
202                                 customLaunchers: {
203                                         ChromeHeadlessNoSandbox: {
204                                                 base: "ChromeHeadless",
205                                                 flags: [ "--no-sandbox" ]
206                                         }
207                                 },
208                                 frameworks: [ "qunit" ],
209                                 middleware: [ "mockserver" ],
210                                 plugins: [
211                                         "karma-*",
212                                         {
213                                                 "middleware:mockserver": [
214                                                         "factory",
215                                                         require( "./test/middleware-mockserver.cjs" )
216                                                 ]
217                                         }
218                                 ],
219                                 client: {
220                                         qunit: {
222                                                 // We're running `QUnit.start()` ourselves via `loadTests()`
223                                                 // in test/jquery.js
224                                                 autostart: false
225                                         }
226                                 },
227                                 files: [
228                                         "test/data/jquery-1.9.1.js",
229                                         "external/sinon/sinon.js",
230                                         "external/npo/npo.js",
231                                         "external/requirejs/require.js",
232                                         "test/data/testinit.js",
234                                         "test/jquery.js",
236                                         {
237                                                 pattern: "dist/jquery.*",
238                                                 included: false,
239                                                 served: true,
240                                                 nocache: true
241                                         },
242                                         {
243                                                 pattern: "src/**",
244                                                 type: "module",
245                                                 included: false,
246                                                 served: true,
247                                                 nocache: true
248                                         },
249                                         { pattern: "external/**", included: false, served: true },
250                                         {
251                                                 pattern: "test/**/*.@(js|css|jpg|html|xml|svg)",
252                                                 included: false,
253                                                 served: true,
254                                                 nocache: true
255                                         }
256                                 ],
257                                 reporters: [ "dots" ],
258                                 autoWatch: false,
260                                 // 2 minutes; has to be longer than QUnit.config.testTimeout
261                                 browserNoActivityTimeout: 120e3,
263                                 concurrency: 3,
264                                 captureTimeout: 20 * 1000,
265                                 singleRun: true
266                         },
267                         main: {
268                                 browsers: customBrowsers ||
269                                         [ "ChromeHeadless", "FirefoxHeadless", "WebkitHeadless" ]
270                         },
271                         esmodules: {
272                                 browsers: customBrowsers || [ "ChromeHeadless" ],
273                                 options: {
274                                         client: {
275                                                 qunit: {
277                                                         // We're running `QUnit.start()` ourselves via `loadTests()`
278                                                         // in test/jquery.js
279                                                         autostart: false,
281                                                         esmodules: true
282                                                 }
283                                         }
284                                 }
285                         },
287                         jsdom: {
288                                 options: {
289                                         files: [
290                                                 "test/data/jquery-1.9.1.js",
291                                                 "test/data/testinit-jsdom.js",
293                                                 // We don't support various loading methods like esmodules,
294                                                 // choosing a version etc. for jsdom.
295                                                 "dist/jquery.js",
297                                                 // A partial replacement for testinit.js#loadTests()
298                                                 "test/data/testrunner.js",
300                                                 // jsdom only runs basic tests
301                                                 "test/unit/basic.js",
303                                                 {
304                                                         pattern: "test/**/*.@(js|css|jpg|html|xml|svg)",
305                                                         included: false,
306                                                         served: true
307                                                 }
308                                         ]
309                                 },
310                                 browsers: [ "jsdom" ]
311                         },
313                         // To debug tests with Karma:
314                         // 1. Run 'grunt karma:chrome-debug' or 'grunt karma:firefox-debug'
315                         //    (any karma subtask that has singleRun=false)
316                         // 2. Press "Debug" in the opened browser window to start
317                         //    the tests. Unlike the other karma tasks, the debug task will
318                         //    keep the browser window open.
319                         "chrome-debug": {
320                                 browsers: [ "Chrome" ],
321                                 singleRun: false
322                         },
323                         "firefox-debug": {
324                                 browsers: [ "Firefox" ],
325                                 singleRun: false
326                         },
327                         "ie-debug": {
328                                 browsers: [ "IE" ],
329                                 singleRun: false
330                         }
331                 },
332                 watch: {
333                         files: [ "<%= eslint.dev.src %>" ],
334                         tasks: [ "dev" ]
335                 },
336                 minify: {
337                         all: {
338                                 files: {
339                                         [ "<%= grunt.option('dist-folder') %>/" +
340                                                 "<%= grunt.option('filename').replace(/\\.js$/, '.min.js') %>" ]:
341                                                 "<%= grunt.option('dist-folder') %>/<%= grunt.option('filename') %>"
342                                 },
343                                 options: {
344                                         sourceMap: {
345                                                 filename: "<%= grunt.option('dist-folder') %>/" +
346                                                         "<%= grunt.option('filename')" +
347                                                         ".replace(/\\.js$/, '.min.map') %>",
349                                                 // The map's `files` & `sources` property are set incorrectly, fix
350                                                 // them via overrides from the task config.
351                                                 // See https://github.com/swc-project/swc/issues/7588#issuecomment-1624345254
352                                                 overrides: {
353                                                         file: "jquery.min.js",
354                                                         sources: [
355                                                                 "jquery.js"
356                                                         ]
357                                                 }
358                                         },
359                                         swc: {
360                                                 format: {
361                                                         ecma: grunt.option( "esm" ) ? 2015 : 5,
362                                                         asciiOnly: true,
363                                                         comments: false,
364                                                         preamble: "/*! jQuery v4.0.0-pre | " +
365                                                                 "(c) OpenJS Foundation and other contributors | " +
366                                                                 "jquery.org/license */\n"
367                                                 },
368                                                 compress: {
369                                                         ecma: grunt.option( "esm" ) ? 2015 : 5,
370                                                         hoist_funs: false,
371                                                         loops: false
372                                                 },
373                                                 mangle: true
374                                         }
375                                 }
376                         }
377                 }
378         } );
380         // Load grunt tasks from NPM packages
381         require( "load-grunt-tasks" )( grunt, {
382                 pattern: nodeV16OrNewer ? [ "grunt-*" ] : [ "grunt-*", "!grunt-eslint" ]
383         } );
385         // Integrate jQuery specific tasks
386         grunt.loadTasks( "build/tasks" );
388         grunt.registerTask( "print_old_node_message", ( ...args ) => {
389                 var task = args.join( ":" );
390                 grunt.log.writeln( "Old Node.js detected, running the task \"" + task + "\" skipped..." );
391         } );
393         grunt.registerTask( "build-all-variants",
394                 "Build all variants of the full/slim build & a script/ESM one",
395                 function() {
396                         const done = this.async();
398                         spawn( "npm run build-all-variants", {
399                                 stdio: "inherit",
400                                 shell: true
401                         } )
402                                 .on( "close", code => {
403                                         done( code === 0 );
404                                 } );
405                 } );
407         grunt.registerTask( "print_jsdom_message", () => {
408                 grunt.log.writeln( "Node.js 17 or newer detected, skipping jsdom tests..." );
409         } );
411         grunt.registerTask( "lint", [
412                 "jsonlint",
414                 // Running the full eslint task without breaking it down to targets
415                 // would run the dist target first which would point to errors in the built
416                 // file, making it harder to fix them. We want to check the built file only
417                 // if we already know the source files pass the linter.
418                 runIfNewNode( "eslint:dev" ),
419                 runIfNewNode( "eslint:dist" )
420         ] );
422         grunt.registerTask( "lint:newer", [
423                 "newer:jsonlint",
425                 // Don't replace it with just the task; see the above comment.
426                 runIfNewNode( "newer:eslint:dev" ),
427                 runIfNewNode( "newer:eslint:dist" )
428         ] );
430         grunt.registerTask( "test:fast", [ "node_smoke_tests:commonjs:jquery" ] );
431         grunt.registerTask( "test:slow", [
432                 runIfNewNode( "promises_aplus_tests" ),
434                 // Support: Node.js 17+
435                 // jsdom fails to connect to the Karma server in Node 17+.
436                 // Until we figure out a fix, skip jsdom tests there.
437                 nodeV17OrNewer ? "print_jsdom_message" : runIfNewNode( "karma:jsdom" )
438         ] );
440         grunt.registerTask( "test:prepare", [
441                 "npmcopy",
442                 "qunit_fixture",
443                 "babel:tests"
444         ] );
446         grunt.registerTask( "test", [
447                 "test:prepare",
448                 "test:fast",
449                 "test:slow"
450         ] );
452         grunt.registerTask( "dev", [
453                 "build:*:*",
454                 runIfNewNode( "newer:eslint:dev" ),
455                 "newer:minify",
456                 "dist:*",
457                 "qunit_fixture",
458                 "compare_size"
459         ] );
461         grunt.registerTask( "default", [
462                 runIfNewNode( "eslint:dev" ),
463                 "build-all-variants",
464                 "test:prepare",
465                 runIfNewNode( "eslint:dist" ),
466                 "test:fast",
467                 "compare_size"
468         ] );