Skip to content

Instantly share code, notes, and snippets.

@choonkeat
Last active October 1, 2019 17:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save choonkeat/b9959168e15d813d9f8a84d0e2c9632a to your computer and use it in GitHub Desktop.
Save choonkeat/b9959168e15d813d9f8a84d0e2c9632a to your computer and use it in GitHub Desktop.
For each file in a directory (recursively) generate an Elm method to return its path, e.g. `Files.imagesLogoSvg`

Given a pubilc/ directory layout

$ tree public
public/
├── images
│   ├── favicon.ico
│   └── logo.55e79e5927a639d21a1b.svg
├── index.html
└── manifest.ad717f2466ce655fff5c.json

1 directory, 4 files

Running files-as-elm-methods.js on it will give us

$ files-as-elm-methods.js public
module Files exposing
	( imagesFaviconIco
	, imagesLogoSvg
	, indexHtml
	, manifestJson
	)

imagesFaviconIco : String
imagesFaviconIco = "/images/favicon.ico"

imagesLogoSvg : String
imagesLogoSvg = "/images/logo.55e79e5927a639d21a1b.svg"

indexHtml : String
indexHtml = "/index.html"

manifestJson : String
manifestJson = "/manifest.ad717f2466ce655fff5c.json"

Then use it like

import Files

view : Html msg
view = img [ src Files.imagesLogoSvg ] []
#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
var listing = {}
function camelCasePath (str) {
var parts = str.split(/[^A-Za-z0-9]/)
if (parts.length === 1) return str
var rest = parts.slice(1)
.filter(function (word) {
return !word.match(/^([a-f0-9]{20}|[a-f0-9]{32})$/) // remove webpack `[hash]`
}).map(function (word) {
if (word.length === 1) return word.toUpperCase()
return word.charAt(0).toUpperCase() + word.substr(1)
})
return [parts[0].toLowerCase(), rest.join('')].join('')
}
function onfiles (trim, basedir, files) {
files.forEach(function (file) {
var fullpath = path.join(basedir, file)
var stat = fs.statSync(fullpath)
if (stat.isDirectory()) {
return onfiles(trim, fullpath, fs.readdirSync(fullpath))
}
listing[camelCasePath(fullpath.substr(trim.length + 1))] = fs.readFileSync(fullpath, 'utf-8')
})
}
process.argv.slice(2).forEach(function (basedir, index) {
onfiles(basedir, basedir, fs.readdirSync(basedir))
})
console.log('module Templates exposing')
Object.keys(listing).forEach(function (key, index) {
var prefix = ' ,'
if (index === 0) prefix = ' ('
console.log(prefix, key)
})
console.log(' )')
Object.keys(listing).forEach(function (key, index) {
console.log('')
console.log('')
console.log(key, ': String')
console.log(key, '=')
console.log(' ' + JSON.stringify(listing[key]))
})
#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
var listing = {}
function camelCasePath (str) {
var parts = str.split(/[^A-Za-z0-9]/)
if (parts.length === 1) return str
var rest = parts.slice(1)
.filter(function (word) {
return !word.match(/^([a-f0-9]{20}|[a-f0-9]{32})$/) // remove webpack `[hash]`
}).map(function (word) {
if (word.length === 1) return word.toUpperCase()
return word.charAt(0).toUpperCase() + word.substr(1)
})
return [parts[0].toLowerCase(), rest.join('')].join('')
}
function onfiles (trim, basedir, files) {
files.forEach(function (file) {
var fullpath = path.join(basedir, file)
var stat = fs.statSync(fullpath)
if (stat.isDirectory()) {
return onfiles(trim, fullpath, fs.readdirSync(fullpath))
}
listing[camelCasePath(fullpath.substr(trim.length + 1))] = fullpath.substr(trim.length)
})
}
process.argv.slice(2).forEach(function (basedir, index) {
onfiles(basedir, basedir, fs.readdirSync(basedir))
})
console.log('module Files exposing')
Object.keys(listing).forEach(function (key, index) {
var prefix = ' ,'
if (index === 0) prefix = ' ('
console.log(prefix, key)
})
console.log(' )')
Object.keys(listing).forEach(function (key, index) {
console.log('')
console.log('')
console.log(key, ': String')
console.log(key, '=')
console.log(' ' + JSON.stringify(listing[key]))
})
generate: src/Files.elm src/Templates.elm
src/Files.elm: files-as-elm-methods.js $(shell find public)
node files-as-elm-methods.js public > src/Files.elm
src/Templates.elm: filecontent-as-elm-methods.js $(shell find public)
node filecontent-as-elm-methods.js public > src/Templates.elm
@choonkeat
Copy link
Author

choonkeat commented Nov 2, 2018

Don't add Files.elm into code repository. Instead, only generate it when necessary: locally, before tests on CI, after building assets before elm make...

import Files

view : Html msg
view = img [ src Files.imagesLogoSvg ] []

With function-level dead code elimination we don't even need to care if the generated Files.elm included references to files that we don't use in our app 🤷‍♂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment