Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(gatsby-plugin-sharp): split single file into more maintainable chunks #10964

Merged
merged 40 commits into from
Mar 11, 2019
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
4f5bbda
feat(sharp): thumbnail creation on demand in dev mode
wardpeet Jan 5, 2019
432eb02
Fix thumbnail generation on gatsby build
wardpeet Jan 10, 2019
6b17365
fix eslint
wardpeet Jan 10, 2019
634c8b4
fix queue caching
wardpeet Jan 10, 2019
579a040
fix queue caching
wardpeet Jan 10, 2019
9c30bbe
move comments
wardpeet Jan 10, 2019
9e73a6a
fix comments
wardpeet Jan 11, 2019
b7fbb53
update cache on query change
wardpeet Jan 24, 2019
b243d0f
update webpack tap name
wardpeet Jan 24, 2019
2869281
review changes
wardpeet Jan 25, 2019
b415b67
review changes
wardpeet Jan 25, 2019
981a02d
fix review
wardpeet Feb 1, 2019
e01844d
make cache smaller & refator toFormat
wardpeet Feb 11, 2019
bd5e801
fix testsf
wardpeet Feb 11, 2019
fbe37e9
fix schedulejob on demand
wardpeet Feb 11, 2019
8e1d714
update snapshots
wardpeet Feb 12, 2019
c6509aa
add tests to opt out of lazy image resolving
wardpeet Feb 12, 2019
1932935
Use schedulejob instead of processFile in devmode
wardpeet Feb 12, 2019
4627ea2
Use schedulejob instead of processFile in devmode
wardpeet Feb 12, 2019
7d23e63
hide status on develop (clutters the cli)
wardpeet Feb 12, 2019
5c7d6f9
update snapshots with jest-path-serializer
wardpeet Feb 12, 2019
f48c6a2
fix workflow
wardpeet Feb 12, 2019
790850f
retrigger ci 🤞
wardpeet Feb 12, 2019
73cc15c
fix azure tests
wardpeet Feb 12, 2019
b860e3a
revert www/README.md
wardpeet Feb 15, 2019
3b0f6d5
remove unused src field
wardpeet Feb 15, 2019
d678998
convert cache to setPluginStatus
wardpeet Feb 15, 2019
62dbc33
add lazyImageGeneration option
wardpeet Feb 15, 2019
c882fea
Merge branch 'master' into feat/lazy-sharp-dev
wardpeet Feb 15, 2019
401add3
fix yarn
wardpeet Feb 15, 2019
03d4e7c
update snapshots
wardpeet Feb 15, 2019
3568d16
Merge branch 'master' into feat/lazy-sharp-dev
wardpeet Feb 25, 2019
13cb9b0
fix image url in test
wardpeet Feb 26, 2019
11c7333
move develop.js to index.js
wardpeet Feb 26, 2019
1422752
revert tests
wardpeet Feb 27, 2019
87981e8
comment out all the lazy stuff
wardpeet Feb 27, 2019
0b0382d
Merge branch 'master' into feat/sharp-refactor
wardpeet Feb 27, 2019
f563913
fix tests
wardpeet Feb 27, 2019
2637cb0
Merge branch 'master' into feat/sharp-refactor
wardpeet Mar 6, 2019
0a614f7
schedule jobs immediately (for now)
pieh Mar 6, 2019
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
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,9 @@ workflows:
requires:
- bootstrap
- integration_tests_long_term_caching:
<<: *ignore_docs
<<: *e2e-test-workflow
- integration_tests_gatsby_pipeline:
<<: *ignore_docs
<<: *e2e-test-workflow
- e2e_tests_gatsbygram:
<<: *e2e-test-workflow
- e2e_tests_path-prefix:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe(`Lazy images`, () => {
const { kill } = await createDevServer()

const response = await request(
`http://localhost:8000/static/6d91c86c0fde632ba4cd01062fd9ccfa/a2541/gatsby-astronaut.png`,
`http://localhost:8000/static/6d91c86c0fde632ba4cd01062fd9ccfa/a484e/gatsby-astronaut.png`,
{
resolveWithFullResponse: true,
}
Expand All @@ -33,18 +33,22 @@ describe(`Lazy images`, () => {
await kill()

expect(response.statusCode).toBe(200)
expect(response.headers[`content-type`]).toBe(`image/png`)

const images = glob.sync(`${basePath}/public/**/*.png`)
expect(images.length).toBe(6)
expect(images.length).toBe(1)
})

test(`should process the rest of images on build`, async () => {
let images = glob.sync(`${basePath}/public/**/*.png`)
expect(images.length).toBe(1)

await execa(`yarn`, [`build`], {
cwd: basePath,
env: { NODE_ENV: `production` },
})

const images = glob.sync(`${basePath}/public/**/*.png`)
images = glob.sync(`${basePath}/public/**/*.png`)
expect(images.length).toBe(6)
})

Expand Down
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module.exports = {
moduleNameMapper: {
"^highlight.js$": `<rootDir>/node_modules/highlight.js/lib/index.js`,
},
snapshotSerializers: [`jest-serializer-path`],
collectCoverage: useCoverage,
coverageReporters: [`json-summary`, `text`, `html`, `cobertura`],
coverageThreshold: {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"jest": "^24.0.0",
"jest-cli": "^24.0.0",
"jest-junit": "^6.1.0",
"jest-serializer-path": "^0.1.15",
"lerna": "^3.10.7",
"lint-staged": "^8.0.4",
"markdown-magic": "^0.1.25",
Expand Down
4 changes: 1 addition & 3 deletions packages/gatsby-plugin-sharp/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
/index.js
/node_modules
/gatsby-node.js
/duotone.js
/*.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ Object {
"aspectRatio": 2.0661764705882355,
"base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABYlAAAWJQFJUiTwAAABAklEQVQoz61R7WrCQBDM+7+Cv1qhlIogoUWMoH8KrfgOLVXRKDV4d7kkl6/pbsxpqBIo9mBuZ7N3k51bB7TKsrwZVsexyS2rKerwFhYFVI1DnkMQOHIuCaLB+ayuwXlMaApXgvMoxjTU8MIQE4rMx8SnNffUkU/qM/bbiPAeRfgw5n8tB+QgO1kmUvwROdm0kYHmUNoe+NoU2wZTdVjQH7Isg9YRpFIXor8vpGmKjb/FQQgslit8fi3wvQ/OHXLhxRvDHY7Qd5+x3e2udmNzTUN4fZvhoddH566L+8cn9AZuVcvpLR3e9kEAISWEkDAmbRXkGCcJ1r4PRRNPEkPu9Kn2Azs0CqJYU3JKAAAAAElFTkSuQmCC",
"density": 144,
"originalImg": "/static/1234/a3030/test.png",
"originalImg": "/static/1234/29cdc/test.png",
"originalName": undefined,
"presentationHeight": 68,
"presentationWidth": 141,
"sizes": "(max-width: 141px) 100vw, 141px",
"src": "/static/1234/a3030/test.png",
"srcSet": "/static/1234/56b42/test.png 200w,
/static/1234/a3030/test.png 281w",
"src": "/static/1234/29cdc/test.png",
"srcSet": "/static/1234/223da/test.png 200w,
/static/1234/29cdc/test.png 281w",
"srcSetType": "image/png",
}
`;
Expand All @@ -32,14 +32,14 @@ Object {
"aspectRatio": 2.0661764705882355,
"base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABYlAAAWJQFJUiTwAAABAklEQVQoz61R7WrCQBDM+7+Cv1qhlIogoUWMoH8KrfgOLVXRKDV4d7kkl6/pbsxpqBIo9mBuZ7N3k51bB7TKsrwZVsexyS2rKerwFhYFVI1DnkMQOHIuCaLB+ayuwXlMaApXgvMoxjTU8MIQE4rMx8SnNffUkU/qM/bbiPAeRfgw5n8tB+QgO1kmUvwROdm0kYHmUNoe+NoU2wZTdVjQH7Isg9YRpFIXor8vpGmKjb/FQQgslit8fi3wvQ/OHXLhxRvDHY7Qd5+x3e2udmNzTUN4fZvhoddH566L+8cn9AZuVcvpLR3e9kEAISWEkDAmbRXkGCcJ1r4PRRNPEkPu9Kn2Azs0CqJYU3JKAAAAAElFTkSuQmCC",
"density": 144,
"originalImg": "/static/1234/67b29/test.png",
"originalImg": "/static/1234/55fae/test.png",
"originalName": undefined,
"presentationHeight": 136,
"presentationWidth": 281,
"sizes": "(max-width: 281px) 100vw, 281px",
"src": "/static/1234/67b29/test.png",
"srcSet": "/static/1234/4df82/test.png 200w,
/static/1234/67b29/test.png 281w",
"src": "/static/1234/55fae/test.png",
"srcSet": "/static/1234/6d94b/test.png 200w,
/static/1234/55fae/test.png 281w",
"srcSetType": "image/png",
}
`;
Expand All @@ -49,13 +49,13 @@ Object {
"aspectRatio": 1,
"base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAGElEQVQ4y2P4TwFgGNU8qnlU86jmgdUMAOBEq42KgAyMAAAAAElFTkSuQmCC",
"density": 72,
"originalImg": "/static/1234/4ce1a/test.png",
"originalImg": "/static/1234/7bd23/test.png",
"originalName": undefined,
"presentationHeight": 1,
"presentationWidth": 1,
"sizes": "(max-width: 1px) 100vw, 1px",
"src": "/static/1234/4ce1a/test.png",
"srcSet": "/static/1234/4ce1a/test.png 1w",
"src": "/static/1234/7bd23/test.png",
"srcSet": "/static/1234/7bd23/test.png 1w",
"srcSetType": "image/png",
}
`;
Expand All @@ -70,3 +70,66 @@ Object {
"width": 746,
}
`;

exports[`gatsby-plugin-sharp queueImageResizing should process immediately when asked 1`] = `
[MockFunction] {
"calls": Array [
Array [
Object {
"args": Object {
"base64": false,
"duotone": false,
"grayscale": false,
"jpegProgressive": true,
"maxWidth": 800,
"pathPrefix": "",
"pngCompressionLevel": 9,
"pngCompressionSpeed": 4,
"quality": 50,
"sizeByPixelDensity": false,
"toFormat": "png",
"width": 3,
},
"inputPath": "<PROJECT_ROOT>/packages/gatsby-plugin-sharp/src/__tests__/images/144-density.png",
"outputPath": "<PROJECT_ROOT>/public/static/1234/39ca0/test.png",
"src": "/static/1234/39ca0/test.png",
},
Object {
"addThirdPartySchema": [Function],
"createJob": [Function],
"createNode": [Function],
"createNodeField": [Function],
"createPage": [Function],
"createPageDependency": [Function],
"createParentChildLink": [Function],
"createRedirect": [Function],
"deleteComponentsDependencies": [Function],
"deleteNode": [Function],
"deleteNodes": [Function],
"deletePage": [Function],
"endJob": [Function],
"replaceComponentQuery": [Function],
"replaceStaticQuery": [Function],
"replaceWebpackConfig": [Function],
"setBabelOptions": [Function],
"setBabelPlugin": [Function],
"setBabelPreset": [Function],
"setJob": [Function],
"setPluginStatus": [Function],
"setWebpackConfig": [Function],
"touchNode": [Function],
},
Object {
"stripMetadata": true,
"useMozJpeg": false,
},
],
],
"results": Array [
Object {
"type": "return",
"value": Promise {},
},
],
}
`;
16 changes: 15 additions & 1 deletion packages/gatsby-plugin-sharp/src/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const path = require(`path`)
const fs = require(`fs-extra`)
jest.mock(`../scheduler`)

jest.mock(`async/queue`, () => () => {
return {
Expand All @@ -16,6 +17,7 @@ const {
queueImageResizing,
getImageSize,
} = require(`../`)
const { scheduleJob } = require(`../scheduler`)

describe(`gatsby-plugin-sharp`, () => {
const args = {
Expand Down Expand Up @@ -50,7 +52,7 @@ describe(`gatsby-plugin-sharp`, () => {
// test name encoding with various characters
const testName = `spaces and '"@#$%^&,`

const queueResult = await queueImageResizing({
const queueResult = queueImageResizing({
file: getFileObject(
path.join(__dirname, `images/144-density.png`),
testName
Expand All @@ -68,6 +70,18 @@ describe(`gatsby-plugin-sharp`, () => {
expect(testName.match(/[!@#$^&," ]/)).not.toBe(false)
expect(queueResultName.match(/[!@#$^&," ]/)).not.toBe(true)
})

it(`should process immediately when asked`, async () => {
scheduleJob.mockResolvedValue(Promise.resolve())
const result = queueImageResizing({
file: getFileObject(path.join(__dirname, `images/144-density.png`)),
args: { width: 3 },
})

await result.finishedPromise

expect(scheduleJob).toMatchSnapshot()
})
})

describe(`fluid`, () => {
Expand Down
90 changes: 88 additions & 2 deletions packages/gatsby-plugin-sharp/src/gatsby-node.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,96 @@
const { setBoundActionCreators, setPluginOptions } = require(`./index`)
const {
setBoundActionCreators,
setPluginOptions,
queue: jobQueue,
reportError,
} = require(`./index`)
const { scheduleJob } = require(`./scheduler`)

exports.onPreInit = ({ actions }, pluginOptions) => {
const getQueueFromCache = async cache => {
const rawQueue = await cache.get(`queue`)

if (!rawQueue) {
return new Map()
}

return new Map(rawQueue)
}

const saveQueueToCache = async (cache, queue) => {
const cachedQueue = await getQueueFromCache(cache)

// merge both queues
for (const [key, job] of cachedQueue) {
if (!queue.has(key)) {
queue.set(key, job)
}
}

// JSON.stringify doesn't work on an Map so we need to convert it to an array
return cache.set(`queue`, Array.from(queue))
wardpeet marked this conversation as resolved.
Show resolved Hide resolved
}

exports.onPreBootstrap = ({ actions }, pluginOptions) => {
setBoundActionCreators(actions)
setPluginOptions(pluginOptions)
}

/**
* save queue to the cache
*/
exports.onPostBootstrap = async ({ cache, store }) =>
saveQueueToCache(cache, jobQueue)

/**
* Execute all unprocessed images on gatsby build
*/
let promises = []
exports.onPreBuild = async ({ cache, boundActionCreators }, pluginOptions) => {
const cachedQueue = await getQueueFromCache(cache)

for (const [, job] of cachedQueue) {
promises.push(scheduleJob(job, boundActionCreators, pluginOptions))
}
}

/**
* wait for all images to be processed
*/
exports.onPostBuild = ({ cache, reporter }) =>
Promise.all(promises)
.then(() => cache.set(`queue`, []))
.catch(({ err, message }) => {
reportError(message || err.message, err, reporter)
})

/**
* Build images on the fly when they are requested by the browser
*/
exports.onCreateDevServer = async (
{ app, cache, emitter, boundActionCreators },
pluginOptions
) => {
emitter.on(`QUERY_QUEUE_DRAINED`, () => saveQueueToCache(cache, jobQueue))

app.use(async (req, res, next) => {
const queue = await getQueueFromCache(cache)
if (!queue.has(req.originalUrl)) {
return next()
}

const job = queue.get(req.originalUrl)

// wait until the file has been processed and saved to disk
await scheduleJob(job, boundActionCreators, pluginOptions, false)
// remove job from queue because it has been processed
queue.delete(req.originalUrl)

await saveQueueToCache(cache, queue)

return res.sendFile(job.outputPath)
})
}

// TODO
// exports.formatJobMessage = jobs => {
// return {
Expand Down
Loading