2019-08-03 13:09:09 +03:00
|
|
|
module assets
|
|
|
|
|
|
|
|
// this module provides an AssetManager for combining
|
|
|
|
// and caching javascript & css.
|
2020-04-26 14:49:31 +03:00
|
|
|
import os
|
|
|
|
import time
|
|
|
|
import crypto.md5
|
2019-08-03 13:09:09 +03:00
|
|
|
|
|
|
|
const (
|
2020-05-22 18:36:09 +03:00
|
|
|
unknown_asset_type_error = 'vweb.assets: unknown asset type'
|
2019-08-03 13:09:09 +03:00
|
|
|
)
|
|
|
|
|
2022-06-19 17:42:22 +03:00
|
|
|
pub struct AssetManager {
|
2019-08-03 13:09:09 +03:00
|
|
|
mut:
|
2021-01-12 06:38:43 +03:00
|
|
|
css []Asset
|
|
|
|
js []Asset
|
2020-03-13 06:32:24 +03:00
|
|
|
pub mut:
|
2019-08-03 13:09:09 +03:00
|
|
|
// when true assets will be minified
|
2021-01-12 06:38:43 +03:00
|
|
|
minify bool
|
2019-08-03 13:09:09 +03:00
|
|
|
// the directory to store the cached/combined files
|
2020-04-26 14:49:31 +03:00
|
|
|
cache_dir string
|
2019-08-03 13:09:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
struct Asset {
|
|
|
|
file_path string
|
|
|
|
last_modified time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
// new_manager returns a new AssetManager
|
2019-10-21 14:14:28 +03:00
|
|
|
pub fn new_manager() &AssetManager {
|
2019-08-03 13:09:09 +03:00
|
|
|
return &AssetManager{}
|
|
|
|
}
|
|
|
|
|
2019-09-06 20:30:20 +03:00
|
|
|
// add_css adds a css asset
|
2020-05-17 14:51:18 +03:00
|
|
|
pub fn (mut am AssetManager) add_css(file string) bool {
|
2019-08-03 13:09:09 +03:00
|
|
|
return am.add('css', file)
|
|
|
|
}
|
|
|
|
|
|
|
|
// add_js adds a js asset
|
2020-05-17 14:51:18 +03:00
|
|
|
pub fn (mut am AssetManager) add_js(file string) bool {
|
2019-08-03 13:09:09 +03:00
|
|
|
return am.add('js', file)
|
|
|
|
}
|
|
|
|
|
|
|
|
// combine_css returns the combined css as a string when to_file is false
|
|
|
|
// when to_file is true it combines the css to disk and returns the path of the file
|
2020-03-13 06:32:24 +03:00
|
|
|
pub fn (am AssetManager) combine_css(to_file bool) string {
|
2019-08-03 13:09:09 +03:00
|
|
|
return am.combine('css', to_file)
|
|
|
|
}
|
|
|
|
|
|
|
|
// combine_js returns the combined js as a string when to_file is false
|
|
|
|
// when to_file is true it combines the css to disk and returns the path of the file
|
2020-03-13 06:32:24 +03:00
|
|
|
pub fn (am AssetManager) combine_js(to_file bool) string {
|
2019-08-03 13:09:09 +03:00
|
|
|
return am.combine('js', to_file)
|
|
|
|
}
|
|
|
|
|
|
|
|
// include_css returns the html <link> tag(s) for including the css files in a page.
|
|
|
|
// when combine is true the files are combined.
|
2020-03-13 06:32:24 +03:00
|
|
|
pub fn (am AssetManager) include_css(combine bool) string {
|
2019-08-03 13:09:09 +03:00
|
|
|
return am.include('css', combine)
|
|
|
|
}
|
|
|
|
|
|
|
|
// include_js returns the html <script> tag(s) for including the js files in a page.
|
|
|
|
// when combine is true the files are combined.
|
2020-03-13 06:32:24 +03:00
|
|
|
pub fn (am AssetManager) include_js(combine bool) string {
|
2019-08-03 13:09:09 +03:00
|
|
|
return am.include('js', combine)
|
|
|
|
}
|
|
|
|
|
2020-03-13 06:32:24 +03:00
|
|
|
fn (am AssetManager) combine(asset_type string, to_file bool) string {
|
2019-08-03 13:09:09 +03:00
|
|
|
if am.cache_dir == '' {
|
|
|
|
panic('vweb.assets: you must set a cache dir.')
|
|
|
|
}
|
|
|
|
cache_key := am.get_cache_key(asset_type)
|
2022-11-15 16:53:13 +03:00
|
|
|
out_file := '${am.cache_dir}/${cache_key}.${asset_type}'
|
2019-08-03 13:09:09 +03:00
|
|
|
mut out := ''
|
2020-04-26 14:49:31 +03:00
|
|
|
// use cache
|
2019-12-04 23:03:12 +03:00
|
|
|
if os.exists(out_file) {
|
2019-08-03 15:16:26 +03:00
|
|
|
if to_file {
|
|
|
|
return out_file
|
2020-04-26 14:49:31 +03:00
|
|
|
}
|
2020-12-27 12:38:12 +03:00
|
|
|
cached := os.read_file(out_file) or { return '' }
|
2019-08-03 15:16:26 +03:00
|
|
|
return cached
|
2019-08-03 13:09:09 +03:00
|
|
|
}
|
|
|
|
// rebuild
|
|
|
|
for asset in am.get_assets(asset_type) {
|
2020-12-27 12:38:12 +03:00
|
|
|
data := os.read_file(asset.file_path) or { return '' }
|
2019-08-03 13:09:09 +03:00
|
|
|
out += data
|
|
|
|
}
|
|
|
|
if am.minify {
|
|
|
|
if asset_type == 'css' {
|
|
|
|
out = minify_css(out)
|
|
|
|
} else {
|
|
|
|
out = minify_js(out)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !to_file {
|
|
|
|
return out
|
|
|
|
}
|
2019-12-04 23:03:12 +03:00
|
|
|
if !os.is_dir(am.cache_dir) {
|
2021-03-01 02:18:14 +03:00
|
|
|
os.mkdir(am.cache_dir) or { panic(err) }
|
2019-08-03 13:09:09 +03:00
|
|
|
}
|
2021-03-01 02:18:14 +03:00
|
|
|
mut file := os.create(out_file) or { panic(err) }
|
|
|
|
file.write(out.bytes()) or { panic(err) }
|
2019-08-03 13:09:09 +03:00
|
|
|
file.close()
|
|
|
|
return out_file
|
|
|
|
}
|
|
|
|
|
2020-03-13 06:32:24 +03:00
|
|
|
fn (am AssetManager) get_cache_key(asset_type string) string {
|
2019-08-03 13:09:09 +03:00
|
|
|
mut files_salt := ''
|
2021-08-04 13:12:02 +03:00
|
|
|
mut latest_modified := i64(0)
|
2019-08-03 13:09:09 +03:00
|
|
|
for asset in am.get_assets(asset_type) {
|
|
|
|
files_salt += asset.file_path
|
2020-02-08 01:12:30 +03:00
|
|
|
if asset.last_modified.unix > latest_modified {
|
|
|
|
latest_modified = asset.last_modified.unix
|
2019-08-03 13:09:09 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
hash := md5.sum(files_salt.bytes()).hex()
|
2022-11-15 16:53:13 +03:00
|
|
|
return '${hash}-${latest_modified}'
|
2019-08-03 13:09:09 +03:00
|
|
|
}
|
|
|
|
|
2020-03-13 06:32:24 +03:00
|
|
|
fn (am AssetManager) include(asset_type string, combine bool) string {
|
2019-08-03 13:09:09 +03:00
|
|
|
assets := am.get_assets(asset_type)
|
|
|
|
mut out := ''
|
|
|
|
if asset_type == 'css' {
|
|
|
|
if combine {
|
|
|
|
file := am.combine(asset_type, true)
|
2022-11-15 16:53:13 +03:00
|
|
|
return '<link rel="stylesheet" href="${file}">\n'
|
2019-08-03 13:09:09 +03:00
|
|
|
}
|
|
|
|
for asset in assets {
|
2022-11-15 16:53:13 +03:00
|
|
|
out += '<link rel="stylesheet" href="${asset.file_path}">\n'
|
2019-08-03 13:09:09 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if asset_type == 'js' {
|
|
|
|
if combine {
|
|
|
|
file := am.combine(asset_type, true)
|
2022-11-15 16:53:13 +03:00
|
|
|
return '<script type="text/javascript" src="${file}"></script>\n'
|
2019-08-03 13:09:09 +03:00
|
|
|
}
|
|
|
|
for asset in assets {
|
2022-11-15 16:53:13 +03:00
|
|
|
out += '<script type="text/javascript" src="${asset.file_path}"></script>\n'
|
2019-08-03 13:09:09 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
// dont return option until size limit is removed
|
2020-05-17 14:51:18 +03:00
|
|
|
// fn (mut am AssetManager) add(asset_type, file string) ?bool {
|
2020-10-21 12:23:03 +03:00
|
|
|
fn (mut am AssetManager) add(asset_type string, file string) bool {
|
2019-12-04 23:03:12 +03:00
|
|
|
if !os.exists(file) {
|
2019-09-25 17:59:50 +03:00
|
|
|
// return error('vweb.assets: cannot add asset $file, it does not exist')
|
2019-08-03 13:09:09 +03:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
asset := Asset{
|
|
|
|
file_path: file
|
2020-10-15 00:39:09 +03:00
|
|
|
last_modified: time.Time{
|
2021-08-04 13:12:02 +03:00
|
|
|
unix: os.file_last_mod_unix(file)
|
2020-10-15 00:39:09 +03:00
|
|
|
}
|
2019-08-03 13:09:09 +03:00
|
|
|
}
|
|
|
|
if asset_type == 'css' {
|
|
|
|
am.css << asset
|
|
|
|
} else if asset_type == 'js' {
|
|
|
|
am.js << asset
|
|
|
|
} else {
|
2022-11-15 16:53:13 +03:00
|
|
|
panic('${assets.unknown_asset_type_error} (${asset_type}).')
|
2019-08-03 13:09:09 +03:00
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-10-21 12:23:03 +03:00
|
|
|
fn (am AssetManager) exists(asset_type string, file string) bool {
|
2019-08-03 13:09:09 +03:00
|
|
|
assets := am.get_assets(asset_type)
|
|
|
|
for asset in assets {
|
|
|
|
if asset.file_path == file {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-03-13 06:32:24 +03:00
|
|
|
fn (am AssetManager) get_assets(asset_type string) []Asset {
|
2019-08-03 13:09:09 +03:00
|
|
|
if asset_type != 'css' && asset_type != 'js' {
|
2022-11-15 16:53:13 +03:00
|
|
|
panic('${assets.unknown_asset_type_error} (${asset_type}).')
|
2019-08-03 13:09:09 +03:00
|
|
|
}
|
2020-10-15 00:39:09 +03:00
|
|
|
assets := if asset_type == 'css' { am.css } else { am.js }
|
2019-08-03 13:09:09 +03:00
|
|
|
return assets
|
|
|
|
}
|
|
|
|
|
|
|
|
// todo: implement proper minification
|
|
|
|
pub fn minify_css(css string) string {
|
|
|
|
mut lines := css.split('\n')
|
|
|
|
for i, _ in lines {
|
|
|
|
lines[i] = lines[i].trim_space()
|
|
|
|
}
|
|
|
|
return lines.join(' ')
|
|
|
|
}
|
|
|
|
|
|
|
|
// todo: implement proper minification
|
|
|
|
pub fn minify_js(js string) string {
|
|
|
|
mut lines := js.split('\n')
|
|
|
|
for i, _ in lines {
|
|
|
|
lines[i] = lines[i].trim_space()
|
|
|
|
}
|
|
|
|
return lines.join(' ')
|
|
|
|
}
|