Skip to content

Instantly share code, notes, and snippets.

@venkatd
Created September 22, 2020 16:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save venkatd/b57b3382e2ed5666d91fc1556f8a1072 to your computer and use it in GitHub Desktop.
Save venkatd/b57b3382e2ed5666d91fc1556f8a1072 to your computer and use it in GitHub Desktop.
Netlify plugin for Flutter
// @ts-check
const fs = require('fs')
const url = require('url')
const path = require('path')
const { promisify } = require('util')
const exists = promisify(fs.exists)
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile)
const rename = promisify(fs.rename)
module.exports = {
/**
* Plugin API
* @param {object} netlifyConfig - Resolved value of Netlify configuration file
* @param {object} pluginConfig - Initial plugin configuration
* @param {object} utils - set of utility functions for working with Netlify
* @param {object} utils.cache - Helper functions for dealing with build cache
* @param {object} utils.git - Helper functions for dealing with git
* @param {object} utils.run - Helper functions for dealing with executables
* @param {object} utils.functions - Helper functions for dealing with Netlify functions
* @param {object} utils.redirects - Helper functions for dealing with Netlify redirects
* @param {object} utils.headers - Helper functions for dealing with Netlify headers
* @param {object} constants - constant values referencing various env paths
* @param {object} constants.CONFIG_PATH - path to netlify config file
* @param {object} constants.BUILD_DIR - path to site build directory
* @param {object} constants.CACHE_DIR - path to cache directory
* @param {object} constants.FUNCTIONS_SRC - path to functions source code directory
* @param {object} constants.FUNCTIONS_DIST - path to functions build directory
* @param {object} api - scoped API instance of Netlify sdk
*/
async onPreBuild({ utils: { run, cache }, constants }) {
const flutterDir = getFlutterDir(constants.CACHE_DIR)
const flutter = getFlutterBinPath(flutterDir)
process.env.FLUTTER_DIR = flutterDir
process.env.FLUTTER_PATH = flutter
await cacheRestoreGitRepo({ run, cache, repoDir: flutterDir })
// const targetFlutterChannel = process.env.FLUTTER_CHANNEL || 'stable'
const targetFlutterVersion = process.env.FLUTTER_VERSION
if (!targetFlutterVersion) throw 'FLUTTER_VERSION is required';
const isFlutterInstalled = await exists(flutter)
const flutterInfo = isFlutterInstalled ? await getFlutterInfo({ run, flutter }) : {}
const currentFlutterVersion = flutterInfo.frameworkVersion
if (isFlutterInstalled && currentFlutterVersion == targetFlutterVersion) {
console.log(`Installed version matches ${currentFlutterVersion} ✅`)
await run(flutter, ['config', '--enable-web'])
return
}
else if (isFlutterInstalled && currentFlutterVersion != targetFlutterVersion) {
console.log(`Flutter installed but installed version ${currentFlutterVersion} doesn't match target ${targetFlutterVersion}`)
}
else if (!isFlutterInstalled) {
console.log(`Flutter is not installed`)
}
await installFlutterFromGit({
run,
version: targetFlutterVersion,
flutterDir: flutterDir
})
await cacheSaveGitRepo({ cache, repoDir: flutterDir })
},
async onBuild({ constants }) {
const fingerprint = process.env.COMMIT_REF
const fingerprintedFileName = `main-${fingerprint}.dart.js`
const htmlPath = path.join(constants.PUBLISH_DIR, 'index.html')
const jsPath = path.join(constants.PUBLISH_DIR, 'main.dart.js')
const newJsPath = path.join(constants.PUBLISH_DIR, fingerprintedFileName)
await rename(jsPath, newJsPath)
console.log({ fingerprintedFileName, htmlPath, jsPath, newJsPath })
await replaceInFile(htmlPath, (html) => {
return html.replace(/main\.dart\.js/g, fingerprintedFileName);
})
},
async onPostBuild({ utils: { run, cache }, constants }) {
const flutterDir = getFlutterDir(constants.CACHE_DIR)
await cacheRestoreGitRepo({ run, cache, repoDir: flutterDir })
}
}
async function getFlutterInfo({ run, flutter }) {
const { stdout } = await run(flutter, ['--version', '--machine']);
return JSON.parse(stdout)
}
function getFlutterDir(cacheDir) { return `${cacheDir}/flutter`; }
async function installFlutterFromGit({ run, version, flutterDir }) {
async function gitClone({ run, source, destination }) {
await run('git', ['clone', source, destination])
}
if (!await exists(flutterDir)) {
await gitClone({ run, source: 'https://github.com/flutter/flutter.git', destination: flutterDir })
}
await run('git', ['-C', flutterDir, 'pull', 'origin', 'master:master'])
const flutter = getFlutterBinPath(flutterDir)
await run('git', ['-C', flutterDir, 'checkout', version])
await run(flutter, ['config', '--enable-web'])
await run(flutter, ['precache', '--no-android', '--no-ios', '--web'])
}
async function installFlutterFromRelease({ run, channel, version, cacheDir, flutterDir }) {
async function downloadFile({ run, source, destination }) {
const fileName = getUrlFileName(source);
await run('wget', ['-q', '--show-progress', '--progress=bar:force', source, '-N', '-P', destination])
return path.join(destination, fileName)
}
function getUrlFileName(uri) {
const parsed = url.parse(uri);
return path.basename(parsed.pathname)
}
function getFlutterPlatform() {
const platform = process.platform;
if (platform == 'darwin') return 'macos'
if (platform == 'win32') return 'windows'
return 'linux'
}
function getFlutterReleaseUrl({ platform, version, channel }) {
const ext = platform == 'linux' ? 'tar.xz' : 'zip'
const fileName = `flutter_${platform}_${version}-${channel}.${ext}`
return `https://storage.googleapis.com/flutter_infra/releases/${channel}/${platform}/${fileName}`;
}
const flutterReleaseUrl = getFlutterReleaseUrl({
platform: getFlutterPlatform(),
channel: channel,
version: version
})
const archivePath = await downloadFile({ run, source: flutterReleaseUrl, destination: cacheDir })
await run('tar', ['xf', archivePath, '--directory', cacheDir])
const flutter = getFlutterBinPath(flutterDir)
await run(flutter, ['config', '--enable-web'])
await run(flutter, ['precache', '--no-android', '--no-ios', '--web'])
}
function getFlutterBinPath(flutterDir) {
return path.join(flutterDir, 'bin', 'flutter')
}
// utils
function gitCacheRenameOp(repoDir) {
return {
from: path.join(repoDir, '.git'),
to: path.join(repoDir, '_git')
}
}
async function cacheSaveGitRepo({ cache, repoDir }) {
const { from, to } = gitCacheRenameOp(repoDir)
await rename(from, to)
await cache.save(repoDir)
await rename(to, from)
}
async function cacheRestoreGitRepo({ run, cache, repoDir }) {
const { from, to } = gitCacheRenameOp(repoDir)
await cache.restore(repoDir)
if (await exists(to)) {
await rename(to, from)
}
}
async function replaceInFile(filepath, callback) {
const contents = await readFile(filepath, 'utf8');
const newContents = callback(contents)
await writeFile(filepath, newContents, 'utf8')
return newContents
}
[[plugins]]
package = "/netlify-plugins/flutter"
[[redirects]]
from = "/index.html"
to = "/*"
status = 200
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment