summaryrefslogtreecommitdiff
path: root/support/make4ht/extensions/make4ht-ext-staticsite.lua
blob: a9a5bcece7cd12025155523471a686f217f0b172 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
local M = {}
local filter = require "make4ht-filter"
local mkutils = require "mkutils"
local log = logging.new "staticsite"

-- get the published file name
local function get_slug(settings)
  local published_name = mkutils.remove_extension(settings.tex_file) .. ".published"
  local config = get_filter_settings "staticsite"
  local file_pattern = config.file_pattern or "%Y-%m-%d-${input}"
  local time = os.time()

  -- we must save the published date, so the subsequent compilations at different days
  -- use the same name
  if mkutils.file_exists(published_name) then
    local f = io.open(published_name, "r")
    local readtime  = f:read("*line")
    time = tonumber(readtime)
    log:info("Already pubslished", os.date("%Y-%m-%d %H:%M", time))
    f:close()
  else
    -- escape 
    -- slug must contain the unescaped input name
    local f = io.open(published_name, "w")
    f:write(time)
    f:close()
  end
  -- set the updated and publishing times
  local updated
  -- the updated time will be set only when it is more than one day from the published time
  local newtime = os.time()
  if (newtime - time) > (24 * 3600) then updated = newtime end
  filter_settings "staticsite" {
    header = {
      time = time,
      updated = updated
    }
  }

  -- make the output file name in the format YYYY-MM-DD-old-filename.html
  local slug = os.date(file_pattern,time) % settings
  return slug
end


-- it is necessary to set correct -jobname in latex_par parameters field
-- in order to the get correct HTML file name
local function update_jobname(slug, latex_par)
  local latex_par = latex_par or ""
  if latex_par:match("%-jobname") then
    local firstchar=latex_par:match("%-jobname=.")
    local replace_pattern="%-jobname=[^%s]+"
    if firstchar == "'" or firstchar=='"' then
      replace_pattern = "%-jobname=".. firstchar .."[^%"..firstchar.."]+"
    end
    
    return latex_par:gsub(replace_pattern, "-jobname=".. slug)
  else
    return latex_par .. "-jobname="..slug
  end
end

-- execute the function passed as parameter only once, when the file matching
-- starts
local function insert_filter(make, pattern, fn)
  local insert_executed = false
  table.insert(make.matches, 1, {
    pattern=pattern,
    params = {},
    command = function()
      if not insert_executed  then
        fn()
      end
      insert_executed = true
    end
  })
end

local function remove_maketitle(make)
  -- use DOM filter to remove \maketitle block
  local domfilter = require "make4ht-domfilter"
  local process = domfilter {
    function(dom)
      local maketitles = dom:query_selector(".maketitle")
      for _, el in ipairs(maketitles) do
        log:debug("removing maketitle")
        el:remove_node()
      end
      return dom
    end
  }
  make:match("html$", process)
end


local function copy_files(filename, par)
  local function prepare_path(dir, subdir)
    local path = dir .. "/" .. subdir .. "/" .. filename
    return path:gsub("//", "/")
  end
  -- get extension settings
  local site_settings = get_filter_settings "staticsite"
  local site_root = site_settings.site_root or "./"
  local map = site_settings.map or {}
  -- default path without subdir, will be used if the file is not matched
  -- by any pattern in the map
  local path = prepare_path(site_root, "")
  for pattern, destination in pairs(map) do
    if filename:match(pattern) then
      path = prepare_path(site_root, destination)
      break
    end
  end
  -- it is possible to use string extrapolation in path, for example for slug
  mkutils.copy(filename, path % par)
end

function M.modify_build(make)
  -- it is necessary to insert the filters for YAML header and file copying as last matches
  -- we use an bogus match which will be executed only once as the very first one to insert
  -- the filters
  -- I should make filter from this
  local process = filter {
    "staticsite"
  }

  -- detect if we should remove maketitle
  local site_settings = get_filter_settings "staticsite"
  -- \maketitle is removed by default, set `remove_maketitle=false` setting to disable that
  if site_settings.remove_maketitle ~= false then
    remove_maketitle(make)
  end

  local settings = make.params
  -- get the published file name
  local slug = get_slug(settings)
  for _, cmd in ipairs(make.build_seq) do
    -- all commands must use the published file name
    cmd.params.input = slug
    cmd.params.latex_par = update_jobname(slug, cmd.params.latex_par)
  end

  local quotepattern = '(['..("%^$().[]*+-?"):gsub("(.)", "%%%1")..'])'
  local mainfile = string.gsub(slug, quotepattern, "%%%1")

  -- run the following code once in the first match on the first file
  insert_filter(make, ".*", function()
    -- for _, match in ipairs(make.matches) do
    --   match.params.outdir = outdir
    --   print(match.pattern, match.params.outdir)
    -- end
    make:match("html?$", process)
    make:match(".*", copy_files, {slug=slug})
  end)

  return make
end

return M