Created
September 22, 2020 16:13
-
-
Save venkatd/b57b3382e2ed5666d91fc1556f8a1072 to your computer and use it in GitHub Desktop.
Netlify plugin for Flutter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[[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