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
9 changes: 1 addition & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: [ 20.x, 22.x, 24.x ]
node-version: [ 22.x, 24.x ]
os: [ windows-latest, ubuntu-latest, macOS-latest ]

# Go
Expand All @@ -40,14 +40,7 @@ jobs:
- name: Install
run: npm install

- name: Test (Node.js <= 16.x)
if: matrix.node-version <= '16.x'
run: npm run test:nolint
env:
CI: true

- name: Test
if: matrix.node-version > '16.x'
run: npm test
env:
CI: true
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
coverage/
node_modules/
package-lock.json
.kiro
.kiro
18 changes: 15 additions & 3 deletions fingerprint/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
let { existsSync } = require('fs')
let { existsSync, globSync } = require('fs')
let fs = require('fs') // Broken out for testing writeFile calls
let { basename, dirname, extname, join, sep } = require('path')

Expand All @@ -7,7 +7,6 @@ let waterfall = require('../run-waterfall')
let pathToUnix = require('../path-to-unix')
let updater = require('../updater')
let sort = require('../path-sort')
let { globSync } = require('../glob')
let sha = require('../sha')

/**
Expand Down Expand Up @@ -61,7 +60,20 @@ module.exports = function fingerprint (params, callback) {
function globFiles (callback) {
let staticAssets = pathToUnix(folder + '/**/*')
try {
let filesFound = globSync(staticAssets, { dot: true, nodir: true, follow: true })
// Node 22 native fs.globSync returns an array of file paths (strings)
// Unlike the glob package, it includes directories, so we need to filter them out
let filesFound = globSync(staticAssets, {
exclude: (name) => name.includes('node_modules'),
})
// Filter out directories (native glob includes them unlike the glob package with nodir: true)
filesFound = filesFound.filter(file => {
try {
return fs.statSync(file).isFile()
}
catch {
return false
}
})
// Renormalize to Unix, otherwise deployments from Windows will fail in Lambda
filesFound = filesFound.map(file => pathToUnix(file))
callback(null, filesFound)
Expand Down
1 change: 0 additions & 1 deletion glob/index.js

This file was deleted.

2 changes: 0 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ let pathToUnix = require('./path-to-unix')
let toLogicalID = require('./to-logical-id')
let updater = require('./updater')
let pathSort = require('./path-sort')
let glob = require('./glob')
let hashid = require('./hashid')

module.exports = {
Expand All @@ -34,6 +33,5 @@ module.exports = {
toLogicalID, // Converts dash casing into Pascal casing for CloudFormation
updater, // Standard Arc status updater and progress indicator
pathSort, // Sort paths
glob, // Glob paths
hashid, // Generate unique IDs from numbers
}
7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@architect/utils",
"version": "5.0.0",
"version": "6.0.0-RC.0",
"description": "Common utility functions",
"main": "index.js",
"repository": {
Expand All @@ -17,18 +17,17 @@
"rc": "npm version prerelease --preid RC"
},
"engines": {
"node": ">=20"
"node": ">=22"
},
"author": "Brian LeRoux <b@brian.io>",
"license": "Apache-2.0",
"dependencies": {
"@aws-lite/client": "^0.21.1",
"glob": "~10.3.12",
"lambda-runtimes": "2.0.5"
},
"devDependencies": {
"@architect/eslint-config": "~3.0.0",
"@architect/inventory": "~4.0.4",
"@architect/inventory": "latest",
"cross-env": "~7.0.3",
"eslint": "~9.1.1",
"proxyquire": "~2.1.3",
Expand Down
75 changes: 33 additions & 42 deletions test/fingerprint/index-test.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
let fs = require('fs')
let { join } = require('path')
let proxyquire = require('proxyquire')
let sha = require('../../sha')
let sinon = require('sinon')
let test = require('tape')
let pathToUnix = require('../../path-to-unix')
let _inventory = require('@architect/inventory')
let inventory
let globStub = sinon.stub().callsFake(() => [])
let glob = { globSync: globStub }
let shaStub = sinon.stub(sha, 'get').callsFake((file, callback) => callback(null, 'df330f3f12')) // Fake hash
let writeFileStub = sinon.stub().callsFake((dest, data, callback) => callback())
let statSyncStub = sinon.stub().callsFake(() => ({ isFile: () => true })) // All paths are files
let shaGetStub = sinon.stub().callsFake((file, callback) => callback(null, 'df330f3f12')) // Fake hash
let fingerprint = proxyquire('../../fingerprint', {
'../glob': glob,
'fs': { ...fs, globSync: globStub, writeFile: writeFileStub, statSync: statSyncStub, '@global': true },
'../sha': { get: shaGetStub },
})

let params = () => ({
Expand All @@ -34,9 +35,6 @@ function updateInventory (t, settings = '', callback) {
}

function reset (t) {
// Reset env for next test
fs.writeFile.restore()
shaStub.resetHistory()
t.pass('Reset')
}

Expand All @@ -52,6 +50,8 @@ test('Set up env', t => {

test('fingerprint respects folder setting', t => {
t.plan(7)
// Reset stubs
shaGetStub.resetHistory()
// Globbing
globStub.resetBehavior()
globStub.callsFake(() => [
Expand All @@ -61,7 +61,8 @@ test('fingerprint respects folder setting', t => {
])
// Static manifest
let manifest
let fsStub = sinon.stub(fs, 'writeFile').callsFake((dest, data, callback) => {
writeFileStub.resetBehavior()
writeFileStub.callsFake((dest, data, callback) => {
manifest = data
callback()
})
Expand All @@ -72,23 +73,22 @@ test('fingerprint respects folder setting', t => {
console.log('Generated manifest:')
console.log(manifest)

t.ok(shaStub.calledTwice, 'Correct number of files hashed')
t.ok(shaGetStub.calledTwice, 'Correct number of files hashed')
t.equal(manifest['index.html'], 'index-df330f3f12.html', 'Manifest data parsed correctly for index.html')
t.equal(result['index.html'], 'index-df330f3f12.html', 'Manifest data returned correctly for index.html')
t.equal(manifest['css/styles.css'], 'css/styles-df330f3f12.css', 'Manifest data parsed correctly for css/styles.css')
t.equal(result['css/styles.css'], 'css/styles-df330f3f12.css', 'Manifest data returned correctly for css/styles.css')
t.ok(fsStub.called, 'static.json manifest written')
t.ok(writeFileStub.called, 'static.json manifest written')

// Reset env for next test
fs.writeFile.restore()
shaStub.resetHistory()
})
})
})

test('fingerprint respects prefix setting (by doing nothing)', t => {
// This test effectively does nothing, but it's here to ensure the presence of prefix does not influence how the static manifest is generated
t.plan(7)
// Reset stubs
shaGetStub.resetHistory()
// Globbing
globStub.resetBehavior()
globStub.callsFake(() => [
Expand All @@ -98,7 +98,8 @@ test('fingerprint respects prefix setting (by doing nothing)', t => {
])
// Static manifest
let manifest
let fsStub = sinon.stub(fs, 'writeFile').callsFake((dest, data, callback) => {
writeFileStub.resetBehavior()
writeFileStub.callsFake((dest, data, callback) => {
manifest = data
callback()
})
Expand All @@ -109,21 +110,19 @@ test('fingerprint respects prefix setting (by doing nothing)', t => {
console.log('Generated manifest:')
console.log(manifest)

t.ok(shaStub.calledTwice, 'Correct number of files hashed')
t.ok(shaGetStub.calledTwice, 'Correct number of files hashed')
t.equal(manifest['index.html'], 'index-df330f3f12.html', 'Manifest data parsed correctly for index.html')
t.equal(result['index.html'], 'index-df330f3f12.html', 'Manifest data returned correctly for index.html')
t.equal(manifest['css/styles.css'], 'css/styles-df330f3f12.css', 'Manifest data parsed correctly for css/styles.css')
t.equal(result['css/styles.css'], 'css/styles-df330f3f12.css', 'Manifest data returned correctly for css/styles.css')
t.ok(fsStub.called, 'static.json manifest written')
t.ok(writeFileStub.called, 'static.json manifest written')

// Reset env for next test
fs.writeFile.restore()
shaStub.resetHistory()
})
})
})

test('fingerprint generates static.json manifest', t => {
shaGetStub.resetHistory()
t.plan(7)
// Globbing
globStub.resetBehavior()
Expand All @@ -134,7 +133,8 @@ test('fingerprint generates static.json manifest', t => {
])
// Static manifest
let manifest
let fsStub = sinon.stub(fs, 'writeFile').callsFake((dest, data, callback) => {
writeFileStub.resetBehavior()
writeFileStub.callsFake((dest, data, callback) => {
manifest = data
callback()
})
Expand All @@ -145,48 +145,40 @@ test('fingerprint generates static.json manifest', t => {
console.log('Generated manifest:')
console.log(manifest)

t.ok(shaStub.calledTwice, 'Correct number of files hashed')
t.ok(shaGetStub.calledTwice, 'Correct number of files hashed')
t.equal(manifest['index.html'], 'index-df330f3f12.html', 'Manifest data parsed correctly for index.html')
t.equal(result['index.html'], 'index-df330f3f12.html', 'Manifest data returned correctly for index.html')
t.equal(manifest['css/styles.css'], 'css/styles-df330f3f12.css', 'Manifest data parsed correctly for css/styles.css')
t.equal(result['css/styles.css'], 'css/styles-df330f3f12.css', 'Manifest data returned correctly for css/styles.css')
t.ok(fsStub.called, 'static.json manifest written')
t.ok(writeFileStub.called, 'static.json manifest written')

// Reset env for next test
fs.writeFile.restore()
shaStub.resetHistory()
})
})
})

test('fingerprint does does not generate static.json when set to external', t => {
shaGetStub.resetHistory()
writeFileStub.resetHistory()
t.plan(4)
// Globbing
globStub.resetBehavior()
globStub.callsFake(() => [
pathToUnix(join(process.cwd(), 'public', 'index.html')),
])
// Static manifest
let fsStub = sinon.stub(fs, 'writeFile').callsFake((dest, data, callback) => {
callback()
})
updateInventory(t, 'fingerprint external', () => {
let p = params()
delete p.fingerprint
fingerprint(p, (err, result) => {
if (err) t.fail(err)
t.notOk(result, 'Did not pass back manifest')
t.ok(shaStub.notCalled, 'No files hashed')
t.ok(fsStub.notCalled, 'static.json manifest not written')

// Reset env for next test
fs.writeFile.restore()
shaStub.resetHistory()
t.ok(shaGetStub.notCalled, 'No files hashed')
t.ok(writeFileStub.notCalled, 'static.json manifest not written')
})
})
})

test('fingerprint ignores specified static assets', t => {
shaGetStub.resetHistory()
t.plan(6)
// Globbing
globStub.resetBehavior()
Expand All @@ -197,7 +189,8 @@ test('fingerprint ignores specified static assets', t => {
])
// Static manifest
let manifest
let fsStub = sinon.stub(fs, 'writeFile').callsFake((dest, data, callback) => {
writeFileStub.resetBehavior()
writeFileStub.callsFake((dest, data, callback) => {
manifest = data
callback()
})
Expand All @@ -208,28 +201,26 @@ test('fingerprint ignores specified static assets', t => {
console.log('Generated manifest:')
console.log(manifest)

t.ok(shaStub.calledOnce, 'Correct number of files hashed')
t.ok(shaGetStub.calledOnce, 'Correct number of files hashed')
t.equal(manifest['index.html'], 'index-df330f3f12.html', 'Manifest data parsed correctly')
t.equal(result['index.html'], 'index-df330f3f12.html', 'Manifest data returned correctly')
t.ok(fsStub.called, 'static.json manifest written')
t.ok(writeFileStub.called, 'static.json manifest written')
reset(t)
})
})
})

test('fingerprint cancels early if disabled', t => {
shaGetStub.resetHistory()
t.plan(3)
updateInventory(t, 'fingerprint false', () => {
let p = params()
delete p.fingerprint
fingerprint(p, (err, result) => {
if (err) t.fail(err)

t.ok(shaStub.notCalled, 'Correct number of files hashed (none)')
t.ok(shaGetStub.notCalled, 'Correct number of files hashed (none)')
t.notOk(result, 'Returned no result')

// Reset env for next test
shaStub.resetHistory()
})
})
})
Expand Down
4 changes: 4 additions & 0 deletions test/mock/fingerprint/foo/static.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"index.html": "index-df330f3f12.html",
"css/styles.css": "css/styles-df330f3f12.css"
}
9 changes: 9 additions & 0 deletions test/package-json-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
let test = require('tape')
let pkg = require('../package.json')

test('Package.json validation for Node.js 22 upgrade', t => {
t.plan(2)

t.equal(pkg.engines.node, '>=22', 'engines.node is set to ">=22"')
t.notOk(pkg.dependencies.glob, 'glob is not in dependencies')
})