js-build-tools 6.6.0 → 6.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/LICENSE +674 -674
  2. package/README.md +768 -701
  3. package/babel.config.js +28 -28
  4. package/dist/build.js +75 -75
  5. package/dist/bundle.js +19 -19
  6. package/dist/bundleMinify.js +7 -7
  7. package/dist/compileReadme.js +4 -4
  8. package/dist/copyFiles.js +17 -17
  9. package/dist/copyFonts.js +4 -4
  10. package/dist/default.js +5 -5
  11. package/dist/dist.js +4 -4
  12. package/dist/distMinify.js +4 -4
  13. package/dist/docs.js +7 -7
  14. package/dist/images.js +4 -4
  15. package/dist/jsdocDocs.js +6 -6
  16. package/dist/lint.js +16 -16
  17. package/dist/partials/addToReadme.js +9 -9
  18. package/dist/partials/beginWatcher.js +4 -4
  19. package/dist/partials/cacheFiles.js +121 -0
  20. package/dist/partials/clean.js +5 -5
  21. package/dist/partials/copyFor.js +9 -9
  22. package/dist/partials/distFor.js +6 -6
  23. package/dist/partials/distForSrc.js +5 -5
  24. package/dist/partials/distSeries.js +7 -7
  25. package/dist/partials/imagesFor.js +155 -93
  26. package/dist/partials/jsdocDocsFor.js +64 -51
  27. package/dist/partials/lintSource.js +51 -51
  28. package/dist/partials/loadPeer.js +99 -99
  29. package/dist/partials/loadPlaywright.js +14 -14
  30. package/dist/partials/minifyFor.js +20 -20
  31. package/dist/partials/neostandardLint.js +175 -175
  32. package/dist/partials/readmeTemplate.js +4 -4
  33. package/dist/partials/removeDirectory.js +11 -11
  34. package/dist/partials/resolveDocsMethod.js +6 -6
  35. package/dist/partials/runOnChange.js +31 -31
  36. package/dist/partials/sassFor.js +6 -6
  37. package/dist/partials/serveStatic.js +9 -9
  38. package/dist/partials/tsFor.js +32 -32
  39. package/dist/partials/typeDocsFor.js +264 -264
  40. package/dist/partials.js +183 -176
  41. package/dist/partials.min.js +1 -1
  42. package/dist/sourceLint.js +18 -18
  43. package/dist/test-helpers/setUp.js +6 -4
  44. package/dist/testFull.js +5 -5
  45. package/dist/testQuick.js +5 -5
  46. package/dist/typeDocs.js +5 -5
  47. package/dist/typeScript.js +4 -4
  48. package/dist/utilities/convertMjsFilesForDocs.js +12 -12
  49. package/dist/utilities/dotGet.js +54 -54
  50. package/dist/utilities/dotNotate.js +79 -79
  51. package/dist/utilities/dotSet.js +45 -45
  52. package/dist/utilities/esmNamedExportsToCjs.js +15 -15
  53. package/dist/utilities/getBundleConfigs.js +9 -9
  54. package/dist/utilities/isObject.js +6 -6
  55. package/dist/utilities/strAfter.js +7 -7
  56. package/dist/utilities/strBefore.js +7 -7
  57. package/dist/watchFull.js +4 -4
  58. package/dist/watchTest.js +4 -4
  59. package/functions/build.mjs +72 -72
  60. package/functions/build.test.mjs +760 -759
  61. package/functions/bundle.mjs +44 -44
  62. package/functions/bundle.test.mjs +219 -219
  63. package/functions/bundleMinify.mjs +16 -16
  64. package/functions/bundleMinify.test.mjs +37 -37
  65. package/functions/compileReadme.mjs +9 -9
  66. package/functions/compileReadme.test.mjs +18 -18
  67. package/functions/copyFiles.mjs +32 -32
  68. package/functions/copyFiles.test.mjs +78 -78
  69. package/functions/copyFonts.mjs +9 -9
  70. package/functions/copyFonts.test.mjs +32 -32
  71. package/functions/default.mjs +12 -12
  72. package/functions/default.test.mjs +33 -33
  73. package/functions/dist.mjs +8 -8
  74. package/functions/dist.test.mjs +44 -44
  75. package/functions/distMinify.mjs +9 -9
  76. package/functions/distMinify.test.mjs +15 -15
  77. package/functions/docs.mjs +13 -13
  78. package/functions/docs.test.mjs +45 -45
  79. package/functions/gulpConfigEntries.test.mjs +23 -23
  80. package/functions/images.mjs +8 -8
  81. package/functions/images.test.mjs +140 -142
  82. package/functions/jsdocDocs.mjs +10 -10
  83. package/functions/jsdocEsmExports.test.mjs +24 -24
  84. package/functions/lint.mjs +9 -9
  85. package/functions/partials/addToReadme.mjs +25 -25
  86. package/functions/partials/addToReadme.test.mjs +34 -34
  87. package/functions/partials/beginWatcher.mjs +9 -9
  88. package/functions/partials/beginWatcher.test.mjs +19 -19
  89. package/functions/partials/cacheFiles.mjs +114 -0
  90. package/functions/partials/cacheFiles.test.mjs +151 -0
  91. package/functions/partials/clean.mjs +13 -13
  92. package/functions/partials/clean.test.mjs +22 -22
  93. package/functions/partials/copyFor.mjs +15 -15
  94. package/functions/partials/copyFor.test.mjs +53 -53
  95. package/functions/partials/distFor.mjs +22 -22
  96. package/functions/partials/distFor.test.mjs +195 -195
  97. package/functions/partials/distForSrc.mjs +11 -11
  98. package/functions/partials/distForSrc.test.mjs +18 -18
  99. package/functions/partials/distSeries.mjs +21 -21
  100. package/functions/partials/distSeries.test.mjs +34 -34
  101. package/functions/partials/imagesFor.mjs +147 -83
  102. package/functions/partials/imagesFor.test.mjs +311 -287
  103. package/functions/partials/jsdocDocsFor.mjs +163 -152
  104. package/functions/partials/jsdocDocsFor.test.mjs +182 -152
  105. package/functions/partials/lintSource.mjs +41 -41
  106. package/functions/partials/loadPeer.mjs +76 -76
  107. package/functions/partials/loadPeer.test.mjs +58 -58
  108. package/functions/partials/loadPlaywright.mjs +23 -23
  109. package/functions/partials/loadPlaywright.test.mjs +70 -70
  110. package/functions/partials/minifyFor.mjs +15 -15
  111. package/functions/partials/minifyFor.test.mjs +49 -49
  112. package/functions/partials/neostandardLint.mjs +142 -142
  113. package/functions/partials/neostandardLint.test.mjs +131 -131
  114. package/functions/partials/readmeTemplate.mjs +12 -12
  115. package/functions/partials/readmeTemplate.test.mjs +34 -34
  116. package/functions/partials/removeDirectory.mjs +34 -34
  117. package/functions/partials/removeDirectory.test.mjs +72 -72
  118. package/functions/partials/resolveDocsMethod.mjs +10 -10
  119. package/functions/partials/resolveDocsMethod.test.mjs +33 -33
  120. package/functions/partials/runOnChange.mjs +54 -54
  121. package/functions/partials/runOnChange.test.mjs +95 -95
  122. package/functions/partials/sassFor.mjs +25 -25
  123. package/functions/partials/sassFor.test.mjs +81 -81
  124. package/functions/partials/serveStatic.mjs +47 -47
  125. package/functions/partials/serveStatic.test.mjs +71 -71
  126. package/functions/partials/tsFor.mjs +92 -92
  127. package/functions/partials/tsFor.test.mjs +88 -88
  128. package/functions/partials/typeDocsFor.mjs +231 -231
  129. package/functions/partials/typeDocsFor.test.mjs +263 -263
  130. package/functions/partials.mjs +32 -31
  131. package/functions/partials.test.mjs +7 -7
  132. package/functions/sass.mjs +4 -4
  133. package/functions/sass.test.mjs +31 -31
  134. package/functions/sourceLint.mjs +13 -13
  135. package/functions/sourceLint.test.mjs +166 -166
  136. package/functions/test-helpers/neostandardLintWithRealNeostandard.mjs +40 -40
  137. package/functions/test-helpers/setUp.mjs +102 -100
  138. package/functions/test-helpers/setUp.test.mjs +19 -19
  139. package/functions/testFull.mjs +17 -17
  140. package/functions/testFull.test.mjs +17 -17
  141. package/functions/testHelpers.mjs +10 -10
  142. package/functions/testHelpers.test.mjs +7 -7
  143. package/functions/testQuick.mjs +17 -17
  144. package/functions/testQuick.test.mjs +17 -17
  145. package/functions/typeDocs.mjs +9 -9
  146. package/functions/typeDocs.test.mjs +11 -11
  147. package/functions/typeScript.mjs +8 -8
  148. package/functions/typeScript.test.mjs +11 -11
  149. package/functions/utilities/convertMjsFilesForDocs.mjs +34 -34
  150. package/functions/utilities/convertMjsFilesForDocs.test.mjs +58 -58
  151. package/functions/utilities/dotGet.mjs +49 -49
  152. package/functions/utilities/dotNotate.mjs +73 -73
  153. package/functions/utilities/dotNotate.test.mjs +41 -41
  154. package/functions/utilities/dotSet.mjs +40 -40
  155. package/functions/utilities/esmNamedExportsToCjs.mjs +30 -30
  156. package/functions/utilities/esmNamedExportsToCjs.test.mjs +47 -47
  157. package/functions/utilities/getBundleConfigs.mjs +30 -30
  158. package/functions/utilities/isObject.mjs +8 -8
  159. package/functions/utilities/strAfter.mjs +12 -12
  160. package/functions/utilities/strBefore.mjs +12 -12
  161. package/functions/watchFull.mjs +9 -9
  162. package/functions/watchFull.test.mjs +41 -41
  163. package/functions/watchTest.mjs +10 -10
  164. package/functions/watchTest.test.mjs +49 -49
  165. package/gulp.config.js +4 -4
  166. package/gulp.config.mjs +349 -340
  167. package/gulpfile.base.js +51 -51
  168. package/gulpfile.base.mjs +29 -29
  169. package/jsdoc.base.js +16 -16
  170. package/jsdoc.esmExports.js +17 -17
  171. package/package.json +146 -145
  172. package/dist/test-helpers/isolateGulpCache.js +0 -25
  173. package/functions/test-helpers/isolateGulpCache.mjs +0 -16
@@ -1,81 +1,81 @@
1
- import fs from 'fs'
2
- import * as setUp from '../test-helpers/setUp.mjs'
3
- import { sassFor } from './sassFor.mjs'
4
- import { countMatches } from 'test-filesystem'
5
-
6
- setUp.setDefaults('test-sass-for')
7
- const gulpConfig = setUp.gulpConfig
8
-
9
- const sassContents = 'html\n' +
10
- ' font-size: 62.5%\n' +
11
- ' color: #444\n' +
12
- ' font-size: 1em\n' +
13
- ' line-height: 1.4\n' +
14
- ' background-color: #fff\n' +
15
- '\n' +
16
- 'html, body\n' +
17
- ' width: 100%\n' +
18
- ' height: 100%\n' +
19
- ' margin: 0\n' +
20
- ' padding: 0\n' +
21
- ' position: absolute\n' +
22
- ' text-align: center\n' +
23
- '\n' +
24
- 'a\n' +
25
- ' outline: none\n' +
26
- ' text-decoration: none'
27
-
28
- const sassPath = gulpConfig.get('sass.path')
29
-
30
- beforeEach(() => setUp.beforeEach().then(() => fs.mkdirSync(sassPath, { recursive: true })))
31
-
32
- afterEach(setUp.afterEach)
33
-
34
- describe('sassFor', () => {
35
- test('copies the sass directory and converts it into css output', done => {
36
- const sassFile = `${sassPath}/sassFor.sass`
37
- fs.writeFileSync(sassFile, sassContents)
38
- expect.assertions(7)
39
- const oldContents = fs.readFileSync(sassFile).toString()
40
- expect(countMatches(oldContents, ';')).toEqual(0)
41
- expect(countMatches(oldContents, ' {')).toEqual(0)
42
- expect(countMatches(oldContents, '\n' +
43
- ' }')).toEqual(0)
44
- const cssPath = gulpConfig.get('sass.to')
45
- sassFor(sassFile)
46
- .on('finish', () => {
47
- expect(fs.existsSync(cssPath)).toBeTruthy()
48
- const cssContents = fs.readFileSync(`${cssPath}/sassFor.css`).toString()
49
- // Will add braces and semicolons
50
- expect(countMatches(cssContents, ';')).toEqual(13)
51
- expect(countMatches(cssContents, '{')).toEqual(3)
52
- expect(countMatches(cssContents, '}')).toEqual(3)
53
- done()
54
- })
55
- .on('error', error => {
56
- console.error('Encountered error', error)
57
- done()
58
- })
59
- })
60
-
61
- test('also writes a minified .min.css next to the css output', done => {
62
- const sassFile = `${sassPath}/sassFor.sass`
63
- fs.writeFileSync(sassFile, sassContents)
64
- expect.assertions(4)
65
- const cssPath = gulpConfig.get('sass.to')
66
- sassFor(sassFile)
67
- .on('finish', () => {
68
- const cssContents = fs.readFileSync(`${cssPath}/sassFor.css`).toString()
69
- const minContents = fs.readFileSync(`${cssPath}/sassFor.min.css`).toString()
70
- expect(minContents.length).toBeLessThan(cssContents.length)
71
- expect(countMatches(minContents.trim(), '\n')).toEqual(0)
72
- expect(minContents).toContain('outline:none')
73
- expect(minContents).toContain('text-align:center')
74
- done()
75
- })
76
- .on('error', error => {
77
- console.error('Encountered error', error)
78
- done()
79
- })
80
- })
81
- })
1
+ import fs from 'fs'
2
+ import * as setUp from '../test-helpers/setUp.mjs'
3
+ import { sassFor } from './sassFor.mjs'
4
+ import { countMatches } from 'test-filesystem'
5
+
6
+ setUp.setDefaults('test-sass-for')
7
+ const gulpConfig = setUp.gulpConfig
8
+
9
+ const sassContents = 'html\n' +
10
+ ' font-size: 62.5%\n' +
11
+ ' color: #444\n' +
12
+ ' font-size: 1em\n' +
13
+ ' line-height: 1.4\n' +
14
+ ' background-color: #fff\n' +
15
+ '\n' +
16
+ 'html, body\n' +
17
+ ' width: 100%\n' +
18
+ ' height: 100%\n' +
19
+ ' margin: 0\n' +
20
+ ' padding: 0\n' +
21
+ ' position: absolute\n' +
22
+ ' text-align: center\n' +
23
+ '\n' +
24
+ 'a\n' +
25
+ ' outline: none\n' +
26
+ ' text-decoration: none'
27
+
28
+ const sassPath = gulpConfig.get('sass.path')
29
+
30
+ beforeEach(() => setUp.beforeEach().then(() => fs.mkdirSync(sassPath, { recursive: true })))
31
+
32
+ afterEach(setUp.afterEach)
33
+
34
+ describe('sassFor', () => {
35
+ test('copies the sass directory and converts it into css output', done => {
36
+ const sassFile = `${sassPath}/sassFor.sass`
37
+ fs.writeFileSync(sassFile, sassContents)
38
+ expect.assertions(7)
39
+ const oldContents = fs.readFileSync(sassFile).toString()
40
+ expect(countMatches(oldContents, ';')).toEqual(0)
41
+ expect(countMatches(oldContents, ' {')).toEqual(0)
42
+ expect(countMatches(oldContents, '\n' +
43
+ ' }')).toEqual(0)
44
+ const cssPath = gulpConfig.get('sass.to')
45
+ sassFor(sassFile)
46
+ .on('finish', () => {
47
+ expect(fs.existsSync(cssPath)).toBeTruthy()
48
+ const cssContents = fs.readFileSync(`${cssPath}/sassFor.css`).toString()
49
+ // Will add braces and semicolons
50
+ expect(countMatches(cssContents, ';')).toEqual(13)
51
+ expect(countMatches(cssContents, '{')).toEqual(3)
52
+ expect(countMatches(cssContents, '}')).toEqual(3)
53
+ done()
54
+ })
55
+ .on('error', error => {
56
+ console.error('Encountered error', error)
57
+ done()
58
+ })
59
+ })
60
+
61
+ test('also writes a minified .min.css next to the css output', done => {
62
+ const sassFile = `${sassPath}/sassFor.sass`
63
+ fs.writeFileSync(sassFile, sassContents)
64
+ expect.assertions(4)
65
+ const cssPath = gulpConfig.get('sass.to')
66
+ sassFor(sassFile)
67
+ .on('finish', () => {
68
+ const cssContents = fs.readFileSync(`${cssPath}/sassFor.css`).toString()
69
+ const minContents = fs.readFileSync(`${cssPath}/sassFor.min.css`).toString()
70
+ expect(minContents.length).toBeLessThan(cssContents.length)
71
+ expect(countMatches(minContents.trim(), '\n')).toEqual(0)
72
+ expect(minContents).toContain('outline:none')
73
+ expect(minContents).toContain('text-align:center')
74
+ done()
75
+ })
76
+ .on('error', error => {
77
+ console.error('Encountered error', error)
78
+ done()
79
+ })
80
+ })
81
+ })
@@ -1,47 +1,47 @@
1
- import fs from 'node:fs'
2
- import http from 'node:http'
3
- import path from 'node:path'
4
-
5
- // A real, previously-undiscovered bug fixed here: without a Content-Type header, a browser enforces strict MIME
6
- // checking on <script type="module"> and refuses to load it at all - exactly the case this partial exists for.
7
- const contentTypes = {
8
- '.css': 'text/css',
9
- '.gif': 'image/gif',
10
- '.html': 'text/html',
11
- '.ico': 'image/x-icon',
12
- '.jpeg': 'image/jpeg',
13
- '.jpg': 'image/jpeg',
14
- '.js': 'text/javascript',
15
- '.json': 'application/json',
16
- '.mjs': 'text/javascript',
17
- '.png': 'image/png',
18
- '.svg': 'image/svg+xml',
19
- '.woff': 'font/woff',
20
- '.woff2': 'font/woff2'
21
- }
22
-
23
- /**
24
- * Serve a directory's real files over plain HTTP, for anything that needs a page loaded from a real URL rather
25
- * than a bare file:// path - most commonly, a page using root-relative asset paths (/browser/...), which a
26
- * file:// load resolves against the filesystem root instead of the page's own directory.
27
- * @memberOf module:partials
28
- * @param {string} rootDir - The directory to serve.
29
- * @param {number} [port=0] - The port to listen on; 0 picks any free port.
30
- * @returns {Promise<{server: http.Server, url: string}>} The running server, and its base URL - close the
31
- * server yourself (server.close()) once done with it.
32
- */
33
- export const serveStatic = (rootDir, port = 0) => new Promise((resolve, reject) => {
34
- const server = http.createServer((req, res) => {
35
- const filePath = path.join(rootDir, decodeURIComponent(req.url.split('?')[0]))
36
- fs.readFile(filePath, (error, data) => {
37
- if (error) {
38
- res.statusCode = 404
39
- return res.end(`Not found: ${req.url}`)
40
- }
41
- res.setHeader('Content-Type', contentTypes[path.extname(filePath)] || 'application/octet-stream')
42
- res.end(data)
43
- })
44
- })
45
- server.once('error', reject)
46
- server.listen(port, '127.0.0.1', () => resolve({ server, url: `http://127.0.0.1:${server.address().port}` }))
47
- })
1
+ import fs from 'node:fs'
2
+ import http from 'node:http'
3
+ import path from 'node:path'
4
+
5
+ // A real, previously-undiscovered bug fixed here: without a Content-Type header, a browser enforces strict MIME
6
+ // checking on <script type="module"> and refuses to load it at all - exactly the case this partial exists for.
7
+ const contentTypes = {
8
+ '.css': 'text/css',
9
+ '.gif': 'image/gif',
10
+ '.html': 'text/html',
11
+ '.ico': 'image/x-icon',
12
+ '.jpeg': 'image/jpeg',
13
+ '.jpg': 'image/jpeg',
14
+ '.js': 'text/javascript',
15
+ '.json': 'application/json',
16
+ '.mjs': 'text/javascript',
17
+ '.png': 'image/png',
18
+ '.svg': 'image/svg+xml',
19
+ '.woff': 'font/woff',
20
+ '.woff2': 'font/woff2'
21
+ }
22
+
23
+ /**
24
+ * Serve a directory's real files over plain HTTP, for anything that needs a page loaded from a real URL rather
25
+ * than a bare file:// path - most commonly, a page using root-relative asset paths (/browser/...), which a
26
+ * file:// load resolves against the filesystem root instead of the page's own directory.
27
+ * @memberOf module:partials
28
+ * @param {string} rootDir - The directory to serve.
29
+ * @param {number} [port=0] - The port to listen on; 0 picks any free port.
30
+ * @returns {Promise<{server: http.Server, url: string}>} The running server, and its base URL - close the
31
+ * server yourself (server.close()) once done with it.
32
+ */
33
+ export const serveStatic = (rootDir, port = 0) => new Promise((resolve, reject) => {
34
+ const server = http.createServer((req, res) => {
35
+ const filePath = path.join(rootDir, decodeURIComponent(req.url.split('?')[0]))
36
+ fs.readFile(filePath, (error, data) => {
37
+ if (error) {
38
+ res.statusCode = 404
39
+ return res.end(`Not found: ${req.url}`)
40
+ }
41
+ res.setHeader('Content-Type', contentTypes[path.extname(filePath)] || 'application/octet-stream')
42
+ res.end(data)
43
+ })
44
+ })
45
+ server.once('error', reject)
46
+ server.listen(port, '127.0.0.1', () => resolve({ server, url: `http://127.0.0.1:${server.address().port}` }))
47
+ })
@@ -1,71 +1,71 @@
1
- import fs from 'fs'
2
- import path from 'node:path'
3
- import * as setUp from '../test-helpers/setUp.mjs'
4
- import { serveStatic } from './serveStatic.mjs'
5
-
6
- setUp.setDefaults('test-serve-static')
7
- const gulpConfig = setUp.gulpConfig
8
-
9
- const write = (file, contents) => {
10
- fs.mkdirSync(path.dirname(file), { recursive: true })
11
- fs.writeFileSync(file, contents)
12
- }
13
-
14
- beforeEach(
15
- () => setUp.beforeEach().then(() => {
16
- const srcPath = gulpConfig.get('srcPath')
17
- write(`${srcPath}/index.html`, '<!doctype html><html><body>hello</body></html>')
18
- write(`${srcPath}/sub/page.html`, '<!doctype html><html><body>nested</body></html>')
19
- write(`${srcPath}/module.js`, 'export default 1')
20
- })
21
- )
22
-
23
- afterEach(setUp.afterEach)
24
-
25
- describe('serveStatic', () => {
26
- test('serves real files from the given directory over a real local HTTP server', async () => {
27
- const { server, url } = await serveStatic(gulpConfig.get('srcPath'))
28
- try {
29
- expect(url).toMatch(/^http:\/\/127\.0\.0\.1:\d+$/)
30
- const root = await fetch(`${url}/index.html`)
31
- expect(await root.text()).toBe('<!doctype html><html><body>hello</body></html>')
32
- const nested = await fetch(`${url}/sub/page.html`)
33
- expect(await nested.text()).toBe('<!doctype html><html><body>nested</body></html>')
34
- } finally {
35
- server.close()
36
- }
37
- })
38
-
39
- test('sends a real Content-Type, not a blank one - a browser refuses to load a <script type="module"> without one', async () => {
40
- const { server, url } = await serveStatic(gulpConfig.get('srcPath'))
41
- try {
42
- const html = await fetch(`${url}/index.html`)
43
- expect(html.headers.get('content-type')).toBe('text/html')
44
- const script = await fetch(`${url}/module.js`)
45
- expect(script.headers.get('content-type')).toBe('text/javascript')
46
- } finally {
47
- server.close()
48
- }
49
- })
50
-
51
- test('responds 404 for a file that does not exist, rather than hanging or throwing', async () => {
52
- const { server, url } = await serveStatic(gulpConfig.get('srcPath'))
53
- try {
54
- const response = await fetch(`${url}/nothing-here.html`)
55
- expect(response.status).toBe(404)
56
- } finally {
57
- server.close()
58
- }
59
- })
60
-
61
- test('two servers for the same directory each get their own free port', async () => {
62
- const first = await serveStatic(gulpConfig.get('srcPath'))
63
- const second = await serveStatic(gulpConfig.get('srcPath'))
64
- try {
65
- expect(first.url).not.toBe(second.url)
66
- } finally {
67
- first.server.close()
68
- second.server.close()
69
- }
70
- })
71
- })
1
+ import fs from 'fs'
2
+ import path from 'node:path'
3
+ import * as setUp from '../test-helpers/setUp.mjs'
4
+ import { serveStatic } from './serveStatic.mjs'
5
+
6
+ setUp.setDefaults('test-serve-static')
7
+ const gulpConfig = setUp.gulpConfig
8
+
9
+ const write = (file, contents) => {
10
+ fs.mkdirSync(path.dirname(file), { recursive: true })
11
+ fs.writeFileSync(file, contents)
12
+ }
13
+
14
+ beforeEach(
15
+ () => setUp.beforeEach().then(() => {
16
+ const srcPath = gulpConfig.get('srcPath')
17
+ write(`${srcPath}/index.html`, '<!doctype html><html><body>hello</body></html>')
18
+ write(`${srcPath}/sub/page.html`, '<!doctype html><html><body>nested</body></html>')
19
+ write(`${srcPath}/module.js`, 'export default 1')
20
+ })
21
+ )
22
+
23
+ afterEach(setUp.afterEach)
24
+
25
+ describe('serveStatic', () => {
26
+ test('serves real files from the given directory over a real local HTTP server', async () => {
27
+ const { server, url } = await serveStatic(gulpConfig.get('srcPath'))
28
+ try {
29
+ expect(url).toMatch(/^http:\/\/127\.0\.0\.1:\d+$/)
30
+ const root = await fetch(`${url}/index.html`)
31
+ expect(await root.text()).toBe('<!doctype html><html><body>hello</body></html>')
32
+ const nested = await fetch(`${url}/sub/page.html`)
33
+ expect(await nested.text()).toBe('<!doctype html><html><body>nested</body></html>')
34
+ } finally {
35
+ server.close()
36
+ }
37
+ })
38
+
39
+ test('sends a real Content-Type, not a blank one - a browser refuses to load a <script type="module"> without one', async () => {
40
+ const { server, url } = await serveStatic(gulpConfig.get('srcPath'))
41
+ try {
42
+ const html = await fetch(`${url}/index.html`)
43
+ expect(html.headers.get('content-type')).toBe('text/html')
44
+ const script = await fetch(`${url}/module.js`)
45
+ expect(script.headers.get('content-type')).toBe('text/javascript')
46
+ } finally {
47
+ server.close()
48
+ }
49
+ })
50
+
51
+ test('responds 404 for a file that does not exist, rather than hanging or throwing', async () => {
52
+ const { server, url } = await serveStatic(gulpConfig.get('srcPath'))
53
+ try {
54
+ const response = await fetch(`${url}/nothing-here.html`)
55
+ expect(response.status).toBe(404)
56
+ } finally {
57
+ server.close()
58
+ }
59
+ })
60
+
61
+ test('two servers for the same directory each get their own free port', async () => {
62
+ const first = await serveStatic(gulpConfig.get('srcPath'))
63
+ const second = await serveStatic(gulpConfig.get('srcPath'))
64
+ try {
65
+ expect(first.url).not.toBe(second.url)
66
+ } finally {
67
+ first.server.close()
68
+ second.server.close()
69
+ }
70
+ })
71
+ })
@@ -1,92 +1,92 @@
1
- import { dest, src } from 'gulp'
2
- import path from 'node:path'
3
- import * as gulpConfig from '../../gulp.config.mjs'
4
- import { importModule, requirePeer } from './loadPeer.mjs'
5
-
6
- // gulp-ts-compile ships ESM-only (no CJS build - see its own package.json), see importModule for how it is imported.
7
- const importGulpTsCompile = () => importModule('gulp-ts-compile')
8
-
9
- /**
10
- * Wrap a stream in a promise that resolves once it finishes writing.
11
- * @memberOf module:partials
12
- * @param {stream.Stream} stream
13
- * @returns {Promise<void>}
14
- */
15
- const installHint = 'Install it in your project with: npm install --save-dev typescript'
16
-
17
- /**
18
- * Load the project's own copy of TypeScript (an optional peer dependency, only needed when the typescript task is
19
- * enabled) from the project being built, so projects which do not use TypeScript never need it installed.
20
- * @memberOf module:partials
21
- * @param {string} [projectPath=process.cwd()] The folder of the project (which has the package.json) being built.
22
- * @returns {Object} The TypeScript compiler API module.
23
- * @throws {Error} With install instructions when typescript is not installed.
24
- */
25
- export const loadTypescript = (projectPath = process.cwd()) => requirePeer('typescript', { task: 'typescript', installHint, projectPath })
26
-
27
- const streamToPromise = (stream) => new Promise((resolve, reject) => {
28
- stream.on('finish', resolve)
29
- stream.on('error', reject)
30
- })
31
-
32
- /**
33
- * Read a tsconfig.json file into real typescript.CompilerOptions values, using only TypeScript's stable, current,
34
- * public config-reading API (the same one `tsc --project` itself uses internally) - never
35
- * `ts.convertCompilerOptionsFromJson`, which is exactly the kind of internal/legacy surface that broke
36
- * gulp-typescript when a removed API call started throwing under TypeScript 7.
37
- * @memberOf module:partials
38
- * @param {string} configPath - Path to a tsconfig.json file.
39
- * @returns {ts.CompilerOptions} TypeScript's compiler options
40
- */
41
- const readCompilerOptions = (ts, configPath) => {
42
- const configFile = ts.readConfigFile(configPath, ts.sys.readFile)
43
- const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configPath))
44
- return parsedConfig.options
45
- }
46
-
47
- /**
48
- * Starting at the source directory, find all the ts files and convert them into the distribution directory.
49
- * @memberOf module:partials
50
- * @param {string|array} [srcPath='']
51
- * @param {string} [distPath='']
52
- * @param {function(): Promise<Function>} [loadTsCompile] - Resolves the `gulp-ts-compile` default export. Real
53
- * callers should never need to pass this - it exists so tests can inject a stand-in compiler, sidestepping the
54
- * genuine dynamic `import()` above that this project's current CommonJS-transform-based Jest setup cannot
55
- * execute without a broader (and riskier) migration to Jest's native ESM support. gulp-ts-compile's own test
56
- * suite already covers real TypeScript-compile correctness - this function's own tests only need to verify its
57
- * config resolution and stream wiring.
58
- * @returns {Function}
59
- */
60
- export const tsFor = (
61
- srcPath = gulpConfig.get('typescript.from'),
62
- distPath = gulpConfig.get('typescript.to'),
63
- loadTsCompile = () => importGulpTsCompile().then(({ default: tsCompile }) => tsCompile)
64
- ) => {
65
- if (gulpConfig.get('typescript.enabled') === false) {
66
- return () => {}
67
- }
68
- const ts = loadTypescript()
69
- const configPath = gulpConfig.get('typescript.config')
70
- // When no tsconfig.json is configured, target/module are pinned explicitly rather than left for TypeScript's
71
- // own compiler defaults - those defaults have already shifted once across a TypeScript version bump in this
72
- // project's own history (silently changing whether output keeps `const`/`let` or downlevels to `var`), and
73
- // pinning them keeps this task's output stable across future TypeScript upgrades too. ES5/CommonJS matches
74
- // what the downstream babel/browserify bundling pipeline has always assumed it receives.
75
- const compilerOptions = configPath
76
- ? readCompilerOptions(ts, configPath)
77
- : { declaration: true, target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS, esModuleInterop: false }
78
- // Accepts gulp's own callback-style task convention (an optional `done`, called once finished) rather than
79
- // just returning a Promise, since callers may invoke the returned function directly with a callback instead
80
- // of relying on gulp's own runner to observe a returned Promise.
81
- return (done) => {
82
- const compile = async () => {
83
- const tsCompile = await loadTsCompile()
84
- const tsResult = src(srcPath).pipe(tsCompile(compilerOptions))
85
- await Promise.all([
86
- streamToPromise(tsResult.dts.pipe(dest(distPath))),
87
- streamToPromise(tsResult.js.pipe(dest(distPath)))
88
- ])
89
- }
90
- return typeof done === 'function' ? compile().then(() => done(), done) : compile()
91
- }
92
- }
1
+ import { dest, src } from 'gulp'
2
+ import path from 'node:path'
3
+ import * as gulpConfig from '../../gulp.config.mjs'
4
+ import { importModule, requirePeer } from './loadPeer.mjs'
5
+
6
+ // gulp-ts-compile ships ESM-only (no CJS build - see its own package.json), see importModule for how it is imported.
7
+ const importGulpTsCompile = () => importModule('gulp-ts-compile')
8
+
9
+ /**
10
+ * Wrap a stream in a promise that resolves once it finishes writing.
11
+ * @memberOf module:partials
12
+ * @param {stream.Stream} stream
13
+ * @returns {Promise<void>}
14
+ */
15
+ const installHint = 'Install it in your project with: npm install --save-dev typescript'
16
+
17
+ /**
18
+ * Load the project's own copy of TypeScript (an optional peer dependency, only needed when the typescript task is
19
+ * enabled) from the project being built, so projects which do not use TypeScript never need it installed.
20
+ * @memberOf module:partials
21
+ * @param {string} [projectPath=process.cwd()] The folder of the project (which has the package.json) being built.
22
+ * @returns {Object} The TypeScript compiler API module.
23
+ * @throws {Error} With install instructions when typescript is not installed.
24
+ */
25
+ export const loadTypescript = (projectPath = process.cwd()) => requirePeer('typescript', { task: 'typescript', installHint, projectPath })
26
+
27
+ const streamToPromise = (stream) => new Promise((resolve, reject) => {
28
+ stream.on('finish', resolve)
29
+ stream.on('error', reject)
30
+ })
31
+
32
+ /**
33
+ * Read a tsconfig.json file into real typescript.CompilerOptions values, using only TypeScript's stable, current,
34
+ * public config-reading API (the same one `tsc --project` itself uses internally) - never
35
+ * `ts.convertCompilerOptionsFromJson`, which is exactly the kind of internal/legacy surface that broke
36
+ * gulp-typescript when a removed API call started throwing under TypeScript 7.
37
+ * @memberOf module:partials
38
+ * @param {string} configPath - Path to a tsconfig.json file.
39
+ * @returns {ts.CompilerOptions} TypeScript's compiler options
40
+ */
41
+ const readCompilerOptions = (ts, configPath) => {
42
+ const configFile = ts.readConfigFile(configPath, ts.sys.readFile)
43
+ const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configPath))
44
+ return parsedConfig.options
45
+ }
46
+
47
+ /**
48
+ * Starting at the source directory, find all the ts files and convert them into the distribution directory.
49
+ * @memberOf module:partials
50
+ * @param {string|array} [srcPath='']
51
+ * @param {string} [distPath='']
52
+ * @param {function(): Promise<Function>} [loadTsCompile] - Resolves the `gulp-ts-compile` default export. Real
53
+ * callers should never need to pass this - it exists so tests can inject a stand-in compiler, sidestepping the
54
+ * genuine dynamic `import()` above that this project's current CommonJS-transform-based Jest setup cannot
55
+ * execute without a broader (and riskier) migration to Jest's native ESM support. gulp-ts-compile's own test
56
+ * suite already covers real TypeScript-compile correctness - this function's own tests only need to verify its
57
+ * config resolution and stream wiring.
58
+ * @returns {Function}
59
+ */
60
+ export const tsFor = (
61
+ srcPath = gulpConfig.get('typescript.from'),
62
+ distPath = gulpConfig.get('typescript.to'),
63
+ loadTsCompile = () => importGulpTsCompile().then(({ default: tsCompile }) => tsCompile)
64
+ ) => {
65
+ if (gulpConfig.get('typescript.enabled') === false) {
66
+ return () => {}
67
+ }
68
+ const ts = loadTypescript()
69
+ const configPath = gulpConfig.get('typescript.config')
70
+ // When no tsconfig.json is configured, target/module are pinned explicitly rather than left for TypeScript's
71
+ // own compiler defaults - those defaults have already shifted once across a TypeScript version bump in this
72
+ // project's own history (silently changing whether output keeps `const`/`let` or downlevels to `var`), and
73
+ // pinning them keeps this task's output stable across future TypeScript upgrades too. ES5/CommonJS matches
74
+ // what the downstream babel/browserify bundling pipeline has always assumed it receives.
75
+ const compilerOptions = configPath
76
+ ? readCompilerOptions(ts, configPath)
77
+ : { declaration: true, target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS, esModuleInterop: false }
78
+ // Accepts gulp's own callback-style task convention (an optional `done`, called once finished) rather than
79
+ // just returning a Promise, since callers may invoke the returned function directly with a callback instead
80
+ // of relying on gulp's own runner to observe a returned Promise.
81
+ return (done) => {
82
+ const compile = async () => {
83
+ const tsCompile = await loadTsCompile()
84
+ const tsResult = src(srcPath).pipe(tsCompile(compilerOptions))
85
+ await Promise.all([
86
+ streamToPromise(tsResult.dts.pipe(dest(distPath))),
87
+ streamToPromise(tsResult.js.pipe(dest(distPath)))
88
+ ])
89
+ }
90
+ return typeof done === 'function' ? compile().then(() => done(), done) : compile()
91
+ }
92
+ }