tshtml-loader 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/export-template.d.ts +2 -2
- package/lib/export-template.js +42 -42
- package/lib/index.d.ts +7 -8
- package/lib/index.js +195 -180
- package/package.json +10 -11
- package/README.md +0 -179
package/lib/export-template.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
export {};
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export {};
|
package/lib/export-template.js
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
exports
|
|
4
|
-
var fs_1 = require("fs");
|
|
5
|
-
var path = require("node:path");
|
|
6
|
-
var index_1 = require("./index");
|
|
7
|
-
// ----------------------------------------------------------------------------------
|
|
8
|
-
if (process.argv.length !== 3) {
|
|
9
|
-
console.error("Please specify one .tshtml file");
|
|
10
|
-
process.exit();
|
|
11
|
-
}
|
|
12
|
-
var fileName = process.argv[2];
|
|
13
|
-
var extension = path.extname(fileName).toLowerCase();
|
|
14
|
-
if (extension !== ".tshtml") {
|
|
15
|
-
console.error("Input file must have .tshtml extension");
|
|
16
|
-
process.exit();
|
|
17
|
-
}
|
|
18
|
-
var fileNameNoExtension = fileName.substr(0, fileName.length - extension.length);
|
|
19
|
-
var outputFileName = fileNameNoExtension + ".html";
|
|
20
|
-
fs_1.readFile(fileName, { encoding: 'utf-8' }, function (err, data) {
|
|
21
|
-
if (!err) {
|
|
22
|
-
// Transform the source
|
|
23
|
-
var result = index_1.executeTemplate(data, path.join(process.cwd(), "/test.html"));
|
|
24
|
-
var htmlResult = index_1.templateToString(result.exports
|
|
25
|
-
// Write to destination
|
|
26
|
-
// process.stdout.write( htmlResult );
|
|
27
|
-
fs_1.writeFile(outputFileName, htmlResult, {
|
|
28
|
-
encoding: "utf8",
|
|
29
|
-
flag: "w"
|
|
30
|
-
}, function (writeErr) {
|
|
31
|
-
if (!writeErr) {
|
|
32
|
-
console.log("File is written: "
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
console.log("Error writing to the output file "
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
console.log("Error reading the input file "
|
|
41
|
-
}
|
|
42
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
var fs_1 = require("fs");
|
|
5
|
+
var path = require("node:path");
|
|
6
|
+
var index_1 = require("./index");
|
|
7
|
+
// ----------------------------------------------------------------------------------
|
|
8
|
+
if (process.argv.length !== 3) {
|
|
9
|
+
console.error("Please specify one .tshtml file");
|
|
10
|
+
process.exit();
|
|
11
|
+
}
|
|
12
|
+
var fileName = process.argv[2];
|
|
13
|
+
var extension = path.extname(fileName).toLowerCase();
|
|
14
|
+
if (extension !== ".tshtml") {
|
|
15
|
+
console.error("Input file must have .tshtml extension");
|
|
16
|
+
process.exit();
|
|
17
|
+
}
|
|
18
|
+
var fileNameNoExtension = fileName.substr(0, fileName.length - extension.length);
|
|
19
|
+
var outputFileName = fileNameNoExtension + ".html";
|
|
20
|
+
(0, fs_1.readFile)(fileName, { encoding: 'utf-8' }, function (err, data) {
|
|
21
|
+
if (!err) {
|
|
22
|
+
// Transform the source
|
|
23
|
+
var result = (0, index_1.executeTemplate)(data, path.join(process.cwd(), "/test.html"));
|
|
24
|
+
var htmlResult = (0, index_1.templateToString)(result.exports.default);
|
|
25
|
+
// Write to destination
|
|
26
|
+
// process.stdout.write( htmlResult );
|
|
27
|
+
(0, fs_1.writeFile)(outputFileName, htmlResult, {
|
|
28
|
+
encoding: "utf8",
|
|
29
|
+
flag: "w",
|
|
30
|
+
}, function (writeErr) {
|
|
31
|
+
if (!writeErr) {
|
|
32
|
+
console.log("File is written: ".concat(outputFileName));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
console.log("Error writing to the output file ".concat(outputFileName), writeErr);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
console.log("Error reading the input file ".concat(fileName), err);
|
|
41
|
+
}
|
|
42
|
+
});
|
package/lib/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export declare function templateToString(builder: any): string;
|
|
1
|
+
import * as webpack from "webpack";
|
|
2
|
+
export default function (this: webpack.LoaderContext<any>, source: string): string;
|
|
3
|
+
export declare function executeTemplate(code: string, fileName: string): {
|
|
4
|
+
exports: any;
|
|
5
|
+
dependencies: string[];
|
|
6
|
+
};
|
|
7
|
+
export declare function templateToString(builder: any): string;
|
package/lib/index.js
CHANGED
|
@@ -1,180 +1,195 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
exports
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
require("
|
|
11
|
-
var
|
|
12
|
-
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
var
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
throw new Error("
|
|
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
|
-
if (
|
|
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
|
-
var
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
var
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = default_1;
|
|
4
|
+
exports.executeTemplate = executeTemplate;
|
|
5
|
+
exports.templateToString = templateToString;
|
|
6
|
+
var lodash_1 = require("lodash");
|
|
7
|
+
var ts_node_1 = require("ts-node");
|
|
8
|
+
var fs_1 = require("fs");
|
|
9
|
+
var path_1 = require("path");
|
|
10
|
+
var vm_1 = require("vm");
|
|
11
|
+
var webpack = require("webpack");
|
|
12
|
+
var tshtml_1 = require("tshtml");
|
|
13
|
+
require("tsconfig-paths/register"); // Necessary to support @folders resolution by Nodejs
|
|
14
|
+
var Module = require("module");
|
|
15
|
+
// ----------------------------------------------------------------------------------
|
|
16
|
+
// tshtml-loader implementation
|
|
17
|
+
function default_1(source) {
|
|
18
|
+
var _this = this;
|
|
19
|
+
var result;
|
|
20
|
+
var htmlResult;
|
|
21
|
+
var builder;
|
|
22
|
+
try {
|
|
23
|
+
result = executeTemplate(source, this.resourcePath);
|
|
24
|
+
builder = result.exports.default;
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
throw new Error("Error executing template: ".concat(error.message, "\n ").concat(error.stack));
|
|
28
|
+
}
|
|
29
|
+
if (builder == null) {
|
|
30
|
+
throw new Error("Template must be exported as \"default\".");
|
|
31
|
+
}
|
|
32
|
+
htmlResult = templateToString(builder);
|
|
33
|
+
// Dependencies
|
|
34
|
+
var reTestNodeFolder = /[\/\\]node_modules[\/\\]/;
|
|
35
|
+
var filteredDependencies = (0, lodash_1.filter)(result.dependencies, function (x) {
|
|
36
|
+
return x.startsWith(_this.rootContext) && !reTestNodeFolder.test(x);
|
|
37
|
+
});
|
|
38
|
+
for (var _i = 0, filteredDependencies_1 = filteredDependencies; _i < filteredDependencies_1.length; _i++) {
|
|
39
|
+
var file = filteredDependencies_1[_i];
|
|
40
|
+
this.addDependency(file);
|
|
41
|
+
}
|
|
42
|
+
// In case of AOT build add the resulting HTML to the assets
|
|
43
|
+
if (this._compilation.name === "angular-compiler:resource") {
|
|
44
|
+
var compilation_1 = this._compilation;
|
|
45
|
+
var rawRequest = this._module.rawRequest;
|
|
46
|
+
var request_1 = rawRequest.substring(0, rawRequest.lastIndexOf("?"));
|
|
47
|
+
// postpone to a later event, because when loader is invoked, the chunks don’t yet exist, and we need to register the file
|
|
48
|
+
compilation_1.hooks.processAssets.tap({
|
|
49
|
+
name: "tshtml-loader",
|
|
50
|
+
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_PRE_PROCESS
|
|
51
|
+
}, function (assets) {
|
|
52
|
+
compilation_1.emitAsset(request_1, new webpack.sources.RawSource(htmlResult));
|
|
53
|
+
compilation_1.addChunk("resource").files.add(request_1);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return htmlResult;
|
|
57
|
+
}
|
|
58
|
+
// ----------------------------------------------------------------------------------
|
|
59
|
+
//
|
|
60
|
+
function executeTemplate(code, fileName) {
|
|
61
|
+
var dirName = (0, path_1.dirname)(fileName);
|
|
62
|
+
var output = compileCode(code, fileName);
|
|
63
|
+
var script = new vm_1.Script(output, { filename: fileName });
|
|
64
|
+
var module = new Module(fileName);
|
|
65
|
+
module.filename = fileName;
|
|
66
|
+
module.loaded = true;
|
|
67
|
+
module.paths = Module._nodeModulePaths(dirName);
|
|
68
|
+
var req = createRequireService(fileName);
|
|
69
|
+
var sandbox = {
|
|
70
|
+
__filename: fileName,
|
|
71
|
+
__dirname: dirName,
|
|
72
|
+
module: module,
|
|
73
|
+
exports: module.exports,
|
|
74
|
+
require: req,
|
|
75
|
+
};
|
|
76
|
+
script.runInNewContext(sandbox, {
|
|
77
|
+
filename: fileName,
|
|
78
|
+
});
|
|
79
|
+
return {
|
|
80
|
+
exports: sandbox.exports,
|
|
81
|
+
dependencies: req.dependencies,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function templateToString(builder) {
|
|
85
|
+
if ((0, lodash_1.isString)(builder)) {
|
|
86
|
+
return builder;
|
|
87
|
+
}
|
|
88
|
+
else if (typeof (builder) == "function") {
|
|
89
|
+
return (new builder()).toString();
|
|
90
|
+
}
|
|
91
|
+
else if ((0, lodash_1.isArray)(builder) || (0, tshtml_1.isTag)(builder)) {
|
|
92
|
+
return (0, tshtml_1.tagToString)(builder);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
return builder.toString();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
var compilerService;
|
|
99
|
+
/***
|
|
100
|
+
* Compiles given TypeScript code
|
|
101
|
+
* @param code
|
|
102
|
+
* @param fileName
|
|
103
|
+
*/
|
|
104
|
+
function compileCode(code, fileName) {
|
|
105
|
+
var tsFileName = "".concat(fileName, ".ts");
|
|
106
|
+
if (compilerService == null) {
|
|
107
|
+
compilerService = (0, ts_node_1.register)({
|
|
108
|
+
compilerOptions: {
|
|
109
|
+
module: "CommonJS",
|
|
110
|
+
target: "es2015",
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return compilerService.compile(code, tsFileName);
|
|
115
|
+
}
|
|
116
|
+
/***
|
|
117
|
+
* Create a logging wrapper for "require" function.
|
|
118
|
+
* @param fileName
|
|
119
|
+
*/
|
|
120
|
+
function createRequireService(fileName) {
|
|
121
|
+
var req = Module.createRequire(fileName);
|
|
122
|
+
var cache = req.cache;
|
|
123
|
+
var dependencies = [];
|
|
124
|
+
var resolveFn = function (id, options) {
|
|
125
|
+
return req.resolve(id, options);
|
|
126
|
+
};
|
|
127
|
+
resolveFn.paths = req.resolve.paths;
|
|
128
|
+
var requireFn = function (request) {
|
|
129
|
+
var filePath;
|
|
130
|
+
try {
|
|
131
|
+
filePath = resolveFn(request);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
// Likely file doesn't exist, so short circuit to the default implementation
|
|
135
|
+
return req(request);
|
|
136
|
+
}
|
|
137
|
+
// Try to get the module from cache and check it's modification time
|
|
138
|
+
var module = cache[filePath];
|
|
139
|
+
if (module != null) {
|
|
140
|
+
cleanOutdatedModules(module, {});
|
|
141
|
+
}
|
|
142
|
+
var result = req(request);
|
|
143
|
+
// Store last modification date
|
|
144
|
+
var allFiles = {};
|
|
145
|
+
storeModuleTimes(cache[filePath], allFiles);
|
|
146
|
+
// Store request to the dependency resolution log
|
|
147
|
+
dependencies.push.apply(dependencies, (0, lodash_1.keys)(allFiles));
|
|
148
|
+
return result;
|
|
149
|
+
};
|
|
150
|
+
requireFn.resolve = resolveFn;
|
|
151
|
+
requireFn.cache = cache;
|
|
152
|
+
requireFn.extensions = req.extensions;
|
|
153
|
+
requireFn.main = req.main;
|
|
154
|
+
requireFn.dependencies = dependencies;
|
|
155
|
+
return requireFn;
|
|
156
|
+
// ---
|
|
157
|
+
function cleanOutdatedModules(module, allFiles) {
|
|
158
|
+
allFiles[module.filename] = true;
|
|
159
|
+
var doClean = false;
|
|
160
|
+
// If the module file was modified since last access -> remove it from the cache
|
|
161
|
+
if (module.fileLastModified != null) {
|
|
162
|
+
var moduleTime = (0, fs_1.statSync)(module.filename).mtimeMs;
|
|
163
|
+
if (module.fileLastModified != moduleTime) {
|
|
164
|
+
doClean = true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Check if any child module is outdated
|
|
168
|
+
for (var _i = 0, _a = module.children; _i < _a.length; _i++) {
|
|
169
|
+
var childModule = _a[_i];
|
|
170
|
+
if (allFiles[childModule.filename] || !checkFilename(childModule.filename))
|
|
171
|
+
continue;
|
|
172
|
+
doClean = cleanOutdatedModules(childModule, allFiles) || doClean;
|
|
173
|
+
}
|
|
174
|
+
if (doClean) {
|
|
175
|
+
delete cache[module.filename];
|
|
176
|
+
}
|
|
177
|
+
return doClean;
|
|
178
|
+
}
|
|
179
|
+
function storeModuleTimes(module, allFiles) {
|
|
180
|
+
allFiles[module.filename] = true;
|
|
181
|
+
if (module.fileLastModified == null) {
|
|
182
|
+
module.fileLastModified = (0, fs_1.statSync)(module.filename).mtimeMs;
|
|
183
|
+
}
|
|
184
|
+
var childModule;
|
|
185
|
+
for (var _i = 0, _a = module.children; _i < _a.length; _i++) {
|
|
186
|
+
childModule = _a[_i];
|
|
187
|
+
if (allFiles[childModule.filename] || !checkFilename(childModule.filename))
|
|
188
|
+
continue;
|
|
189
|
+
storeModuleTimes(childModule, allFiles);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function checkFilename(filename) {
|
|
193
|
+
return filename.indexOf("\\node_modules\\") === -1;
|
|
194
|
+
}
|
|
195
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tshtml-loader",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"files": [
|
|
@@ -8,22 +8,21 @@
|
|
|
8
8
|
],
|
|
9
9
|
"bin": "./lib/export-template.js",
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsc ./src/index.ts ./src/export-template.ts --outdir ./lib -d
|
|
12
|
-
"build-watch": "tsc ./src/index.ts --outdir ./lib -d -w"
|
|
13
|
-
"copy-docs": "npx npx shx cp ../README.md ."
|
|
11
|
+
"build": "tsc ./src/index.ts ./src/export-template.ts --outdir ./lib -d",
|
|
12
|
+
"build-watch": "tsc ./src/index.ts --outdir ./lib -d -w"
|
|
14
13
|
},
|
|
15
14
|
"author": "LOGEX Group",
|
|
16
15
|
"license": "MIT",
|
|
17
16
|
"devDependencies": {
|
|
18
|
-
"@types/lodash": "4.
|
|
19
|
-
"@types/node": "
|
|
20
|
-
"@types/webpack": "
|
|
17
|
+
"@types/lodash": "4.17.15",
|
|
18
|
+
"@types/node": "22.13.5",
|
|
19
|
+
"@types/webpack": "5.28.5"
|
|
21
20
|
},
|
|
22
21
|
"dependencies": {
|
|
23
22
|
"lodash": "^4.17.21",
|
|
24
|
-
"ts-node": "^10.
|
|
25
|
-
"tsconfig-paths": "^
|
|
26
|
-
"tshtml": "^1.
|
|
27
|
-
"typescript": "
|
|
23
|
+
"ts-node": "^10.9.2",
|
|
24
|
+
"tsconfig-paths": "^4.2.0",
|
|
25
|
+
"tshtml": "^1.3.0",
|
|
26
|
+
"typescript": "5.7.3"
|
|
28
27
|
}
|
|
29
28
|
}
|
package/README.md
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
# tshtml
|
|
2
|
-
|
|
3
|
-
## Installation
|
|
4
|
-
```
|
|
5
|
-
npm i tshtml tshtml-loader
|
|
6
|
-
```
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## What is it?
|
|
10
|
-
tshtml-file is a TypeScript template that emits HTML code during build-time.
|
|
11
|
-
The simplest one could look like this:
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
// test.tshtml
|
|
15
|
-
|
|
16
|
-
export default `<p>Hello world!</p>`;
|
|
17
|
-
```
|
|
18
|
-
Obviosly this does not differ much from a static HTML file with the same paragraph.
|
|
19
|
-
But now you can start write code in the template, just like this:
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
export default `
|
|
23
|
-
<p>Hello world!</p>
|
|
24
|
-
<p>Build time is ${Date()}.</p>`;
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
This code is executed at build time, and only the resulting
|
|
28
|
-
HTML will be fed to Angular. This way we produce just the same fast Angular templates,
|
|
29
|
-
but with help of possible very sophisticated code in tshtml.
|
|
30
|
-
So, it's a lovely little metaprogramming: code that creates code.
|
|
31
|
-
|
|
32
|
-
You can write helper functions to extract repetitive patterns of template code,
|
|
33
|
-
import constants and static data structures used at run time to generate some
|
|
34
|
-
template elements out of them, and also create templates that inherit other templates.
|
|
35
|
-
The latter is very handy when you have Angular components derived from some
|
|
36
|
-
base components.
|
|
37
|
-
|
|
38
|
-
## _html_ function
|
|
39
|
-
Combining HTML by hands is not very pleasant task. You must ensure that it is well-formed
|
|
40
|
-
so usually it requires more efforts than just to concatenate strings together.
|
|
41
|
-
|
|
42
|
-
To simplify this task we created `html` function. It parses the text into a
|
|
43
|
-
tree of node-like objects that can be later serialized to a well-formed HTML.
|
|
44
|
-
Since the tree is just a graph of plain JavaScript objects, you can update it:
|
|
45
|
-
change attributes, add new children and so on.
|
|
46
|
-
|
|
47
|
-
The simplest example with `html` added looks basically the same:
|
|
48
|
-
```
|
|
49
|
-
import { html } from "tshtml";
|
|
50
|
-
|
|
51
|
-
export default html`<p>Hello world!</p>`;
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Let's combine two HTML trees together:
|
|
55
|
-
```
|
|
56
|
-
import { html } from "tshtml";
|
|
57
|
-
|
|
58
|
-
const buildTime = html`<p>Build time is ${Date()}.</p>`;
|
|
59
|
-
|
|
60
|
-
// Resulting tree has buildTime subtree inserted at the appropriate place
|
|
61
|
-
const res = html`
|
|
62
|
-
<p>Hello world!</p>
|
|
63
|
-
${buildTime}`;
|
|
64
|
-
|
|
65
|
-
// Tree of template elements will be converted to string by the loader
|
|
66
|
-
export default res;
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Here `builtTime` is not a string, it's an object, but you can still use standard
|
|
70
|
-
template string placeholder syntax to combine HTML fragments together.
|
|
71
|
-
|
|
72
|
-
The tree can be manipulated using methods of `TemplateItem` class.
|
|
73
|
-
```
|
|
74
|
-
import { htmlEl } from "tshtml";
|
|
75
|
-
|
|
76
|
-
const res = htmlEl`<p>Hello world!</p>`;
|
|
77
|
-
|
|
78
|
-
// Add an attribute
|
|
79
|
-
res.attrs( { "color": "red } );
|
|
80
|
-
|
|
81
|
-
export default res;
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## To use in your Angular project
|
|
85
|
-
First, configure Angular build as described here:
|
|
86
|
-
https://codeburst.io/customizing-angular-cli-6-build-an-alternative-to-ng-eject-a48304cd3b21
|
|
87
|
-
|
|
88
|
-
For that you need to install necessary packages:
|
|
89
|
-
```
|
|
90
|
-
npm install @angular-devkit/build-angular --save-dev
|
|
91
|
-
npm install @angular-builders/custom-webpack --save-dev
|
|
92
|
-
```
|
|
93
|
-
Add "architect" section to the "angular.json" if it is not already there. Change
|
|
94
|
-
builder configuration and specify the name of additional webpack config file:
|
|
95
|
-
```
|
|
96
|
-
"architect": {
|
|
97
|
-
"build": {
|
|
98
|
-
"builder": "@angular-builders/custom-webpack:browser",
|
|
99
|
-
"options": {
|
|
100
|
-
"customWebpackConfig": {
|
|
101
|
-
"path": "./extra-webpack.config.js",
|
|
102
|
-
"replaceDuplicatePlugins": true
|
|
103
|
-
},
|
|
104
|
-
...
|
|
105
|
-
},
|
|
106
|
-
"serve": {
|
|
107
|
-
"builder": "@angular-builders/custom-webpack:dev-server",
|
|
108
|
-
...
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
Add `extra-webpack.config.js`. Configuration is a bit different depending of which
|
|
112
|
-
Webpack version do you use.
|
|
113
|
-
|
|
114
|
-
**Webpack 4**
|
|
115
|
-
|
|
116
|
-
With Webpack 4 (used in Angular 11 and earlier) you need to configure loader like this.
|
|
117
|
-
It's preferred to use `tshtml-loader` version less than 1.2 with Webpack 4.
|
|
118
|
-
```
|
|
119
|
-
module.exports = {
|
|
120
|
-
module: {
|
|
121
|
-
rules: [
|
|
122
|
-
{
|
|
123
|
-
test: /\.tshtml$/,
|
|
124
|
-
use: ["tshtml-loader"],
|
|
125
|
-
enforce: "pre"
|
|
126
|
-
},
|
|
127
|
-
]
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
**Webpack 5**
|
|
134
|
-
|
|
135
|
-
Webpack 5 (used in Angular 12 and up) brings new resource types, so the recommended loader configuration is the following:
|
|
136
|
-
```
|
|
137
|
-
module.exports = {
|
|
138
|
-
module: {
|
|
139
|
-
rules: [
|
|
140
|
-
test: /\.tshtml$/,
|
|
141
|
-
use: ["tshtml-loader"],
|
|
142
|
-
type: "asset/source"
|
|
143
|
-
]
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
You also need to have "tshtml" and "tshtml-loader" packages in your project.
|
|
150
|
-
|
|
151
|
-
Finally you just refer to `tshtml` files instead of `html` files in your Angular components.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
## Library development
|
|
155
|
-
|
|
156
|
-
In tshtml folder:
|
|
157
|
-
```
|
|
158
|
-
npm run test
|
|
159
|
-
npm run build
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### Debugging
|
|
163
|
-
|
|
164
|
-
If you need to debug the tests, then you can do the following:
|
|
165
|
-
|
|
166
|
-
1. In the console where you are going to execute tests set environment variable:
|
|
167
|
-
`set-item env:NODE_OPTIONS "--inspect"`
|
|
168
|
-
|
|
169
|
-
1. Configure you debugger to attach to Node.js at port 9229. Start the debugger
|
|
170
|
-
|
|
171
|
-
1. Start test: `node ./test/jasmine "TEST NAME"`
|
|
172
|
-
|
|
173
|
-
### tshtml-loader
|
|
174
|
-
|
|
175
|
-
In tshtml-loader folder:
|
|
176
|
-
```
|
|
177
|
-
npm run build
|
|
178
|
-
```
|
|
179
|
-
|