Vue.js
JAMstack
Gridsome

Gridsome 0.2.5コードリーディング

GridsomeはVueでサイトを構築していくGatsbyJSライクな静的サイトジェネレーター。

関連記事: GatsbyJSライクなVueの静的サイトジェネレーター Gridsomeを触ってみた - mottox2 blog

注意

0.2.5の段階のコードなので、大きく変わっている可能性あり

執筆時点のコードはここから見れます

https://github.com/gridsome/gridsome/tree/568207fbc413c1b9bd4ff1a671e063ba540b5001


CLI部分

CLI単体では、createやhelpなどのプロジェクトに依存しないコマンドを定義している。

gridsomeのプロジェクトであればrequireでgridsomeのindex.jsを呼び出している。


/packages/cli/bin/gridsome.js

#!/usr/bin/env node


program
.version(require('../package').version)
.usage('<command> [options]')

program
.command('create <name> [starter]')
.description('create a new website')
.action((...args) => {
return wrapCommand(create)(...args)
})

try {
const gridsomePath = resolveCwd.silent('gridsome')

if (gridsomePath) {
// eslint-disable-next-line
require(gridsomePath)({ context, program })
}
} catch (err) {
console.log(err)
}

// show a warning if the command does not exist
program.arguments('<command>').action((command) => {
console.log(chalk.red(`Unknown command ${chalk.bold(command)}`))
})


上のCLIでrequireされるファイル。developやbuildなどのコマンド定義を呼び出している。

ここからはdevelopやbuildなどの実行を行う。


/gridsome/index.js

module.exports = ({ context, program }) => {

program
.command('develop')
.description('start development server')
.option('-p, --port <port>', 'use specified port (default: 8080)')
.option('-h, --host <host>', 'use specified host (default: 0.0.0.0)')
.action(args => {
wrapCommand(require('./lib/develop'))(context, args)
})

program
.command('build')
.description('build site for production')
.action(() => {
wrapCommand(require('./lib/build'))(context, {})
})


ビルド用のファイル。重要なのはcreateApp。この中でpluginを読み込んだり、ページの情報を読み込んでいたりする。


/gridsome/lib/build.js

module.exports = async (context, args) => {

process.env.NODE_ENV = 'production'
process.env.GRIDSOME_MODE = 'static'

const buildTime = hirestime()
const app = await createApp(context, { args })
const { config, graphql } = app

await app.dispatch('beforeBuild', { context, config })
await fs.ensureDir(config.cacheDir)
await fs.remove(config.outDir)

const queue = await createRenderQueue(app)

// 1. run all GraphQL queries and save results into json files
await app.dispatch('beforeRenderQueries', () => ({ context, config, queue }))
await renderPageQueries(queue, graphql)

// 2. compile assets with webpack
await compileAssets(app)

// 3. render a static index.html file for each possible route
await app.dispatch('beforeRenderHTML', () => ({ context, config, queue }))
await renderHTML(queue, config)

// 4. process queued images
await app.dispatch('beforeProcessAssets', () => ({ context, config, queue: app.queue }))
await processFiles(app.queue.files, config)
await processImages(app.queue.images, config)

// 5. copy static files
if (fs.existsSync(config.staticDir)) {
await fs.copy(config.staticDir, config.targetDir)
}

// 6. clean up
await app.dispatch('afterBuild', () => ({ context, config, queue }))
await fs.remove(path.resolve(config.cacheDir, 'data'))
await fs.remove(config.manifestsDir)

console.log()
console.log(` Done in ${buildTime(hirestime.S)}s`)
console.log()
}


new App()された後、bootstrapが呼ばれる。

bootstrap内でいろいろやっている。


/gridsome/lib/app/App.js

class App {

constructor (context, options) {
process.GRIDSOME = this

this.events = []
this.clients = {}
this.plugins = []
this.context = context
this.config = loadConfig(context, options)
this.isInitialized = false
this.isBootstrapped = false

autoBind(this)
}

async bootstrap (phase) {
const bootstrapTime = hirestime()

const phases = [
{ title: 'Initialize', run: this.init },
{ title: 'Load sources', run: this.loadSources },
{ title: 'Create GraphQL schema', run: this.createSchema },
{ title: 'Generate code', run: this.generateFiles }
]

console.log(`Gridsome v${version}`)
console.log()

for (const current of phases) {
if (phases.indexOf(current) <= phase) {
const timer = hirestime()
await current.run(this)

console.info(`${current.title} - ${timer(hirestime.S)}s`)
}
}

console.info(`Bootstrap finish - ${bootstrapTime(hirestime.S)}s`)

this.isBootstrapped = true

return this
}

...
}


入り口の重要な部分だけ取り上げた。各機能に興味があると次のようなファイルから見るといいかもしれない。


  • 開発環境の処理に興味があれば gridsome/lib/dev.js

  • Pluginに興味があれば gridsome/lib/app/App.js のinitの読み込み処理とgridsome/lib/app/PluginAPI.jsあたり

  • GraphQLの処理しているデータは gridsome/lib/graphql/PluginStore.jsあたり。これらが各プラグインで呼ばれているイメージ

  • vueファイル内のpage-queryのwebpackの設定は gridsome/lib/webpack/loaders/page-query.js