vite-plugin-php 1.0.71 → 2.0.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/dist/index.mjs CHANGED
@@ -1,17 +1,45 @@
1
- import require$$0$4, { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from 'fs';
2
- import require$$0$1, { relative, dirname, resolve } from 'path';
3
- import { normalizePath } from 'vite';
4
- import http from 'node:http';
5
- import { spawn } from 'child_process';
1
+ import { spawn } from 'node:child_process';
6
2
  import { fileURLToPath } from 'url';
3
+ import { resolve, dirname, relative } from 'node:path';
7
4
  import require$$0 from 'os';
5
+ import require$$0$1 from 'path';
8
6
  import require$$0$2 from 'util';
9
7
  import require$$0$3 from 'stream';
10
8
  import require$$0$5 from 'events';
9
+ import require$$0$4 from 'fs';
10
+ import { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from 'node:fs';
11
+ import http from 'node:http';
12
+ import { normalizePath } from 'vite';
11
13
 
12
- function makeID() {
13
- return Date.now().toString(36) + Math.random() * 100;
14
- }
14
+ const EPHPError = {
15
+ ERROR: 1,
16
+ WARNING: 2,
17
+ PARSE: 4,
18
+ NOTICE: 8,
19
+ CORE_ERROR: 16,
20
+ CORE_WARNING: 32,
21
+ COMPILE_ERROR: 64,
22
+ COMPILE_WARNING: 128,
23
+ USER_ERROR: 256,
24
+ USER_WARNING: 512,
25
+ USER_NOTICE: 1024,
26
+ STRICT: 2048,
27
+ RECOVERABLE_ERROR: 4096,
28
+ DEPRECATED: 8192,
29
+ USER_DEPRECATED: 16384,
30
+ ALL: 32767
31
+ };
32
+
33
+ const internalParam = "__314159265359__";
34
+ const shared = {
35
+ viteConfig: void 0,
36
+ devConfig: {
37
+ cleanup: true,
38
+ errorLevels: EPHPError.ALL | EPHPError.STRICT
39
+ },
40
+ entries: [],
41
+ tempDir: ".php-tmp"
42
+ };
15
43
 
16
44
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
17
45
 
@@ -100,136 +128,130 @@ picocolors.exports.createColors = createColors;
100
128
  var picocolorsExports = picocolors.exports;
101
129
  const colors = /*@__PURE__*/getDefaultExportFromCjs(picocolorsExports);
102
130
 
103
- function resolveEnvPrefix({ envPrefix = "VITE_" }) {
104
- envPrefix = Array.isArray(envPrefix) ? envPrefix : [envPrefix];
105
- if (envPrefix.includes("")) {
106
- throw new Error(
107
- `envPrefix option contains value '', which could lead unexpected exposure of sensitive information.`
108
- );
131
+ function hasViteConfig(input) {
132
+ if (!input) {
133
+ throw new Error("Vite config not initialized!");
109
134
  }
110
- return envPrefix;
111
- }
112
- const envPattern = /%(\S+?)%/g;
113
- function initReplaceEnv(config) {
114
- const envPrefix = resolveEnvPrefix({ envPrefix: config.envPrefix });
115
- const env = { ...config.env };
116
- for (const key in config.define) {
117
- if (key.startsWith(`import.meta.env.`)) {
118
- const val = config.define[key];
119
- if (typeof val === "string") {
120
- try {
121
- const parsed = JSON.parse(val);
122
- env[key.slice(16)] = typeof parsed === "string" ? parsed : val;
123
- } catch {
124
- env[key.slice(16)] = val;
125
- }
126
- } else {
127
- env[key.slice(16)] = JSON.stringify(val);
128
- }
129
- }
130
- }
131
- return function replaceEnv(content, filename) {
132
- return content.replace(envPattern, (text, key) => {
133
- if (key in env) {
134
- return env[key];
135
- } else {
136
- if (envPrefix.some((prefix) => key.startsWith(prefix))) {
137
- const relativeHtml = normalizePath(
138
- relative(config.root, filename)
139
- );
140
- config.logger.warn(
141
- colors.yellow(
142
- colors.bold(
143
- `(!) ${text} is not defined in env variables found in /${relativeHtml}. Is the variable mistyped?`
144
- )
145
- )
146
- );
147
- }
148
- return text;
149
- }
150
- });
151
- };
152
135
  }
153
136
 
154
- function writeFile(file, data) {
155
- mkdirSync(dirname(file), { recursive: true });
156
- writeFileSync(file, data);
137
+ function log(message, { type = "info", prefix = true, ...options } = {}) {
138
+ hasViteConfig(shared.viteConfig);
139
+ let output = "";
140
+ if (prefix) {
141
+ output += colors.bgMagenta(colors.white("\u2009php\u2009")) + " ";
142
+ }
143
+ output += message;
144
+ shared.viteConfig.logger[type](output, options);
157
145
  }
158
-
159
- const phpTagPattern = /<\?(?:php|).+?(\?>|$)/gis;
160
- function escapePHP({ inputFile, config }) {
161
- const replaceEnv = initReplaceEnv(config);
162
- const input = readFileSync(inputFile, "utf-8").toString();
163
- const phpCodes = {};
164
- const isJS = inputFile.includes(".js") || inputFile.includes(".ts");
165
- const isML = inputFile.includes(".php") || inputFile.includes(".htm");
166
- const escapedCode = input.replace(phpTagPattern, (match) => {
167
- let token = makeID();
168
- if (isJS) {
169
- token = `/*${token}*/`;
170
- } else if (isML) {
171
- token = `\u2400\u2400${token}\u2400\u2400`;
172
- }
173
- phpCodes[token] = replaceEnv(match, inputFile);
174
- return token;
175
- });
176
- return {
177
- escapedCode,
178
- phpCodes,
179
- write(outputFile) {
180
- writeFile(outputFile, escapedCode);
181
- writeFile(outputFile + ".json", JSON.stringify(phpCodes));
146
+ log.error = function(json, options = {}) {
147
+ let output = json;
148
+ let type = "info";
149
+ try {
150
+ const data = JSON.parse(json);
151
+ output = "";
152
+ switch (data.code) {
153
+ case EPHPError.PARSE:
154
+ case EPHPError.ERROR:
155
+ case EPHPError.CORE_ERROR:
156
+ case EPHPError.COMPILE_ERROR:
157
+ case EPHPError.USER_ERROR:
158
+ type = "error";
159
+ output += colors.bgRedBright(colors.white("Fatal Error"));
160
+ break;
161
+ case EPHPError.WARNING:
162
+ case EPHPError.USER_WARNING:
163
+ case EPHPError.COMPILE_WARNING:
164
+ case EPHPError.RECOVERABLE_ERROR:
165
+ type = "warn";
166
+ output += colors.yellowBright("Warning");
167
+ break;
168
+ case EPHPError.NOTICE:
169
+ case EPHPError.USER_NOTICE:
170
+ type = "info";
171
+ output += colors.bgWhite(colors.black("Notice"));
172
+ break;
173
+ case EPHPError.STRICT:
174
+ type = "info";
175
+ output += colors.yellow("Strict");
176
+ break;
177
+ case EPHPError.DEPRECATED:
178
+ case EPHPError.USER_DEPRECATED:
179
+ type = "info";
180
+ output += colors.cyan("Deprecated");
181
+ break;
182
+ default:
183
+ break;
182
184
  }
183
- };
184
- }
185
- function unescapePHP({ escapedCode, phpCodes }) {
186
- let out = escapedCode;
187
- Object.entries(phpCodes).forEach(([token, code]) => {
188
- out = out.replace(token, (match) => {
189
- return `${code}`;
190
- });
191
- });
192
- return out;
193
- }
185
+ output += " " + data.message + "\r\n";
186
+ output += " In: " + data.file.replace(
187
+ resolve(shared.tempDir),
188
+ shared.viteConfig?.root
189
+ ) + "\r\n";
190
+ output += " On line: " + data.line;
191
+ } catch (error) {
192
+ }
193
+ log(output, { ...options, type });
194
+ };
194
195
 
196
+ const PHP_Server = {
197
+ binary: "php",
198
+ port: 65535,
199
+ process: void 0,
200
+ start,
201
+ stop
202
+ };
195
203
  function start(root) {
196
- if (!globalThis.php?.pid) {
204
+ if (!PHP_Server.process?.pid) {
197
205
  const routerFileUrl = new URL("./router.php", import.meta.url);
198
- globalThis.php = spawn(phpServer.binary, [
206
+ PHP_Server.process = spawn(PHP_Server.binary, [
199
207
  "-S",
200
- "localhost:" + phpServer.port,
208
+ "localhost:" + PHP_Server.port,
201
209
  "-t",
202
210
  root,
203
211
  fileURLToPath(routerFileUrl)
204
212
  ]).once("spawn", () => {
205
- console.log(
206
- `PHP development server started (PID: ${globalThis.php?.pid})`
207
- );
213
+ log(`Server started (PID: ${PHP_Server.process?.pid})`);
208
214
  }).on("error", (error) => {
209
- console.error("PHP dev-server error: " + error.message);
210
- }).on("close", (code) => {
211
- console.log(`PHP development server stopped (Code: ${code})`);
215
+ log(`Server error: ${error.message})`, {
216
+ type: "error"
217
+ });
218
+ });
219
+ PHP_Server.process.stdout?.on("data", (data) => {
220
+ log("", { timestamp: true });
221
+ `${data}`.trim().split("\r\n").forEach((line) => {
222
+ if (line.startsWith(internalParam + ":")) {
223
+ log.error(
224
+ line.substring((internalParam + ":").length),
225
+ { prefix: false }
226
+ );
227
+ } else {
228
+ log(line);
229
+ }
230
+ });
212
231
  });
213
232
  }
214
233
  }
215
- function stop() {
216
- if (globalThis.php) {
217
- globalThis.php.stdin?.destroy();
218
- globalThis.php.stdout?.destroy();
219
- globalThis.php.stderr?.destroy();
220
- if (globalThis.php.kill()) {
221
- console.log("PHP development server killed");
234
+ function stop(callBack) {
235
+ if (PHP_Server.process) {
236
+ PHP_Server.process.on("close", (code) => {
237
+ log("Ended with: " + code);
238
+ PHP_Server.process = void 0;
239
+ callBack();
240
+ });
241
+ PHP_Server.process.stdin?.destroy();
242
+ PHP_Server.process.stdout?.destroy();
243
+ PHP_Server.process.stderr?.destroy();
244
+ if (PHP_Server.process.kill()) {
245
+ log("Shutting down");
222
246
  } else {
223
- console.error("Attention! Failed to kill PHP development server!");
247
+ log("Failed to send SIGTERM", {
248
+ type: "error"
249
+ });
224
250
  }
251
+ } else {
252
+ callBack();
225
253
  }
226
254
  }
227
- const phpServer = {
228
- binary: "php",
229
- port: 65535,
230
- start,
231
- stop
232
- };
233
255
 
234
256
  var tasks = {};
235
257
 
@@ -6948,7 +6970,8 @@ var out = FastGlob;
6948
6970
 
6949
6971
  const fastGlob = /*@__PURE__*/getDefaultExportFromCjs(out);
6950
6972
 
6951
- function consoleHijack(entries) {
6973
+ function consoleHijack() {
6974
+ const entries = shared.entries;
6952
6975
  ["log", "info", "warn", "error"].forEach((command) => {
6953
6976
  const cx = console[command];
6954
6977
  console[command] = function(...args) {
@@ -6964,307 +6987,457 @@ function consoleHijack(entries) {
6964
6987
  });
6965
6988
  }
6966
6989
 
6967
- const namespacePattern = new RegExp(
6968
- /(.+?)(<\?(?:php|)\s+namespace\s\S+?(?:\s*;|\s*{).+)(<\/.+?>|<.+?\/>)(.+|$)/,
6990
+ function readFile(file) {
6991
+ return readFileSync(file, "utf-8").toString();
6992
+ }
6993
+ function writeFile(file, data) {
6994
+ mkdirSync(dirname(file), { recursive: true });
6995
+ writeFileSync(file, data);
6996
+ }
6997
+
6998
+ function makeID() {
6999
+ return Date.now().toString(36) + Math.random() * 100;
7000
+ }
7001
+
7002
+ function resolveEnvPrefix({ envPrefix = "VITE_" }) {
7003
+ envPrefix = Array.isArray(envPrefix) ? envPrefix : [envPrefix];
7004
+ if (envPrefix.includes("")) {
7005
+ throw new Error(
7006
+ `envPrefix option contains value '', which could lead unexpected exposure of sensitive information.`
7007
+ );
7008
+ }
7009
+ return envPrefix;
7010
+ }
7011
+ const envPattern = /%(\S+?)%/g;
7012
+ function initReplaceEnv() {
7013
+ hasViteConfig(shared.viteConfig);
7014
+ const { env, define, root, logger } = shared.viteConfig;
7015
+ const envPrefix = resolveEnvPrefix({
7016
+ envPrefix: shared.viteConfig.envPrefix
7017
+ });
7018
+ for (const key in define) {
7019
+ if (key.startsWith(`import.meta.env.`)) {
7020
+ const val = define[key];
7021
+ if (typeof val === "string") {
7022
+ try {
7023
+ const parsed = JSON.parse(val);
7024
+ env[key.slice(16)] = typeof parsed === "string" ? parsed : val;
7025
+ } catch {
7026
+ env[key.slice(16)] = val;
7027
+ }
7028
+ } else {
7029
+ env[key.slice(16)] = JSON.stringify(val);
7030
+ }
7031
+ }
7032
+ }
7033
+ return function replaceEnv(content, filename) {
7034
+ const relativeHtml = normalizePath(relative(root, filename));
7035
+ return content.replace(envPattern, (text, key) => {
7036
+ if (key in env) {
7037
+ return env[key];
7038
+ } else {
7039
+ if (envPrefix.some((prefix) => key.startsWith(prefix))) {
7040
+ logger.warn(
7041
+ colors.yellow(
7042
+ colors.bold(
7043
+ `(!) ${text} is not defined in env variables found in /${relativeHtml}. Is the variable mistyped?`
7044
+ )
7045
+ )
7046
+ );
7047
+ }
7048
+ return text;
7049
+ }
7050
+ });
7051
+ };
7052
+ }
7053
+
7054
+ var __defProp = Object.defineProperty;
7055
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7056
+ var __publicField = (obj, key, value) => {
7057
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
7058
+ return value;
7059
+ };
7060
+ const phpStartPattern = `<\\?(?:php|)`;
7061
+ const phpEndPattern = `\\?>`;
7062
+ const phpTagRegex = new RegExp(
7063
+ `${phpStartPattern}.+?(${phpEndPattern}|$)`,
7064
+ "gis"
7065
+ );
7066
+ class PHP_Code {
7067
+ constructor(code) {
7068
+ __publicField(this, "file", "!!__VIRTUAL__!!.php");
7069
+ __publicField(this, "code");
7070
+ __publicField(this, "mapping", {});
7071
+ this.code = code;
7072
+ return this;
7073
+ }
7074
+ static fromFile(file) {
7075
+ const inst = new this(readFile(file));
7076
+ inst.file = file;
7077
+ return inst;
7078
+ }
7079
+ applyEnv() {
7080
+ const replaceEnv = initReplaceEnv();
7081
+ this.code = replaceEnv(this.code, this.file || "");
7082
+ return this;
7083
+ }
7084
+ escape() {
7085
+ const isJS = this.file.includes(".js") || this.file.includes(".ts");
7086
+ const isML = this.file.includes(".php") || this.file.includes(".htm");
7087
+ this.code = this.code.replace(phpTagRegex, (match) => {
7088
+ let token = makeID();
7089
+ if (isJS) {
7090
+ token = `/*${token}*/`;
7091
+ } else if (isML) {
7092
+ token = `\u2400\u2400${token}\u2400\u2400`;
7093
+ }
7094
+ this.mapping[token] = match;
7095
+ return token;
7096
+ });
7097
+ return this;
7098
+ }
7099
+ write(file, mapping = false) {
7100
+ writeFile(file, this.code);
7101
+ mapping && writeFile(file + ".json", JSON.stringify(this.mapping));
7102
+ }
7103
+ static unescape(code, mapping) {
7104
+ let out = code;
7105
+ Object.entries(mapping).forEach(([token, code2]) => {
7106
+ out = out.replace(token, code2);
7107
+ });
7108
+ return out;
7109
+ }
7110
+ }
7111
+
7112
+ const serve = {
7113
+ rewriteUrl: (url) => url
7114
+ };
7115
+ function tempName(entry) {
7116
+ return `${shared.tempDir}/${entry}`;
7117
+ }
7118
+ const servePlugin = {
7119
+ name: "serve-php",
7120
+ apply: "serve",
7121
+ enforce: "post",
7122
+ configResolved() {
7123
+ function handleExit(signal) {
7124
+ if (signal === "SIGINT") {
7125
+ console.log();
7126
+ }
7127
+ const tempDir = resolve(shared.tempDir);
7128
+ if (shared.devConfig.cleanup && existsSync(tempDir)) {
7129
+ log("Removing temporary files");
7130
+ rmSync(tempDir, {
7131
+ recursive: true,
7132
+ force: true
7133
+ });
7134
+ }
7135
+ if (PHP_Server.process && shared.viteConfig?.command === "serve") {
7136
+ PHP_Server.stop(() => {
7137
+ process.exit();
7138
+ });
7139
+ } else {
7140
+ process.exit();
7141
+ }
7142
+ }
7143
+ [
7144
+ "exit",
7145
+ "SIGINT",
7146
+ "SIGUSR1",
7147
+ "SIGUSR2",
7148
+ "uncaughtException",
7149
+ "SIGTERM"
7150
+ ].forEach((eventType) => {
7151
+ new Promise((resolve2) => process.on(eventType, resolve2)).then(
7152
+ handleExit
7153
+ );
7154
+ });
7155
+ const gitIgnoreFile = resolve(`${shared.tempDir}/.gitignore`);
7156
+ if (!existsSync(gitIgnoreFile)) {
7157
+ writeFile(gitIgnoreFile, "*\r\n**/*");
7158
+ }
7159
+ shared.entries.forEach((entry) => {
7160
+ PHP_Code.fromFile(entry).applyEnv().write(tempName(entry));
7161
+ });
7162
+ },
7163
+ configureServer(server) {
7164
+ if (!PHP_Server.process) {
7165
+ PHP_Server.start(server?.config.root);
7166
+ }
7167
+ server.middlewares.use(async (req, res, next) => {
7168
+ try {
7169
+ if (req.url && !["/@vite", "/@fs", "/@id/__x00__", "/node_modules"].some(
7170
+ (path) => req.url.startsWith(path)
7171
+ )) {
7172
+ req.on("error", (error) => {
7173
+ throw error;
7174
+ });
7175
+ const url = new URL(req.url, "http://localhost");
7176
+ if (shared.viteConfig?.server.port) {
7177
+ url.port = shared.viteConfig.server.port.toString();
7178
+ }
7179
+ const requestUrl = url.pathname;
7180
+ if (url.pathname.endsWith("/")) {
7181
+ url.pathname += "index.php";
7182
+ }
7183
+ const routedUrl = serve.rewriteUrl(url);
7184
+ if (routedUrl) {
7185
+ url.pathname = routedUrl.pathname;
7186
+ url.search = routedUrl.search;
7187
+ url.hash = routedUrl.hash;
7188
+ }
7189
+ const entryPathname = url.pathname.substring(1);
7190
+ const entry = shared.entries.find((file) => {
7191
+ return file === entryPathname || file.substring(0, file.lastIndexOf(".")) === entryPathname;
7192
+ });
7193
+ if (entry) {
7194
+ const tempFile = tempName(entry);
7195
+ if (existsSync(resolve(tempFile))) {
7196
+ url.pathname = tempFile;
7197
+ url.port = PHP_Server.port.toString();
7198
+ url.searchParams.set(
7199
+ internalParam,
7200
+ new URLSearchParams({
7201
+ $REQUEST_URI: requestUrl,
7202
+ $PHP_SELF: "/" + entry,
7203
+ temp_dir: shared.tempDir,
7204
+ error_levels: shared.devConfig.errorLevels.toString()
7205
+ }).toString()
7206
+ );
7207
+ const body = await new Promise(
7208
+ (resolve2, reject) => {
7209
+ let data = [];
7210
+ req.on("data", (chunk) => {
7211
+ data.push(chunk);
7212
+ }).on("end", () => {
7213
+ resolve2(Buffer.concat(data));
7214
+ });
7215
+ }
7216
+ );
7217
+ const phpResult = await new Promise(async (resolve2, reject) => {
7218
+ const chunks = [];
7219
+ let statusCode;
7220
+ let incomingHeaders = {};
7221
+ const request = http.request(
7222
+ url.toString(),
7223
+ {
7224
+ method: req.method,
7225
+ headers: {
7226
+ ...req.headers,
7227
+ "content-length": Buffer.byteLength(body)
7228
+ }
7229
+ },
7230
+ (msg) => {
7231
+ statusCode = msg.statusCode;
7232
+ incomingHeaders = msg.headers;
7233
+ msg.on("data", (data) => {
7234
+ chunks.push(data);
7235
+ });
7236
+ }
7237
+ ).on("error", (error) => {
7238
+ reject(error);
7239
+ }).on("close", () => {
7240
+ const content = Buffer.concat(chunks).toString(
7241
+ "utf8"
7242
+ );
7243
+ resolve2({
7244
+ statusCode,
7245
+ headers: incomingHeaders,
7246
+ content
7247
+ });
7248
+ });
7249
+ request.write(body, (error) => {
7250
+ if (error) {
7251
+ reject(error);
7252
+ }
7253
+ });
7254
+ request.end();
7255
+ });
7256
+ let out = phpResult.content;
7257
+ if (phpResult.headers["content-type"]?.includes(
7258
+ "html"
7259
+ )) {
7260
+ out = await server.transformIndexHtml(
7261
+ `/${entry}.html`,
7262
+ out,
7263
+ requestUrl
7264
+ );
7265
+ }
7266
+ res.writeHead(
7267
+ phpResult.statusCode || 200,
7268
+ phpResult.headers
7269
+ ).end(out);
7270
+ return;
7271
+ }
7272
+ }
7273
+ }
7274
+ } catch (error) {
7275
+ console.error("Vite-PHP Error: " + error);
7276
+ }
7277
+ next();
7278
+ });
7279
+ },
7280
+ handleHotUpdate({ server, file }) {
7281
+ const entry = shared.entries.find(
7282
+ (entryFile) => resolve(entryFile) === resolve(file)
7283
+ );
7284
+ if (entry) {
7285
+ PHP_Code.fromFile(entry).applyEnv().write(tempName(entry));
7286
+ server.moduleGraph.invalidateAll();
7287
+ }
7288
+ if (entry || !file.startsWith(resolve(shared.tempDir)) && file.includes(".php")) {
7289
+ server.ws.send({
7290
+ type: "full-reload"
7291
+ });
7292
+ }
7293
+ }
7294
+ };
7295
+
7296
+ const assetsPattern = new RegExp(
7297
+ `^(.+?)(${phpStartPattern}\\s+namespace\\s\\S+?(?:\\s*;|\\s*{).+)$`,
6969
7298
  "si"
6970
7299
  );
6971
- function processOutput(input) {
7300
+ const lastTagPattern = new RegExp(`^(.+(?:</.+?>|<.+?/>))(.+|)$`, "si");
7301
+ const closingPattern = new RegExp(`^(.+)(${phpStartPattern}.+)$`, "si");
7302
+ function fixAssetsInjection(input) {
6972
7303
  let out = input;
6973
- out = out.replace(
6974
- namespacePattern,
6975
- (match, assets, contents, lastTag, trailingContent) => contents + lastTag + "\r\n" + assets.trim() + trailingContent
6976
- );
7304
+ let assets = "";
7305
+ out = out.replace(assetsPattern, (match, p1, p2) => {
7306
+ assets = p1.trim();
7307
+ return p2;
7308
+ });
7309
+ const injectAssets = (_, part1, part2) => {
7310
+ let a = assets;
7311
+ if (!(part1.endsWith("\n") || part1.endsWith("\r"))) {
7312
+ a = "\r\n" + a;
7313
+ }
7314
+ if (!(part2.startsWith("\n") || part2.startsWith("\r"))) {
7315
+ a += "\r\n";
7316
+ }
7317
+ assets = "";
7318
+ return `${part1}${a}${part2}`;
7319
+ };
7320
+ if (assets) {
7321
+ out = out.replace(lastTagPattern, injectAssets);
7322
+ }
7323
+ if (assets) {
7324
+ out = out.replace(closingPattern, injectAssets);
7325
+ }
7326
+ if (assets) {
7327
+ out += assets;
7328
+ }
6977
7329
  return out;
6978
7330
  }
6979
7331
 
6980
- const internalParam = "__314159265359__";
7332
+ const buildPlugin = {
7333
+ name: "build-php",
7334
+ apply: "build",
7335
+ enforce: "pre",
7336
+ resolveId(source, importer, options) {
7337
+ if (shared.entries.includes(source)) {
7338
+ return {
7339
+ // Rename ids because Vite transforms only .html files: https://github.com/vitejs/vite/blob/0cde495ebeb48bcfb5961784a30bfaed997790a0/packages/vite/src/node/plugins/html.ts#L330
7340
+ id: `${source}.html`,
7341
+ resolvedBy: "vite-plugin-php",
7342
+ meta: {
7343
+ originalId: source
7344
+ }
7345
+ };
7346
+ }
7347
+ },
7348
+ load(id, options) {
7349
+ const moduleInfo = this.getModuleInfo(id);
7350
+ const entry = moduleInfo?.meta.originalId;
7351
+ if (entry) {
7352
+ const php = PHP_Code.fromFile(entry).applyEnv().escape();
7353
+ return {
7354
+ code: php.code,
7355
+ meta: { phpMapping: php.mapping }
7356
+ };
7357
+ }
7358
+ },
7359
+ generateBundle: {
7360
+ order: "post",
7361
+ handler(options, bundle, isWrite) {
7362
+ Object.entries(bundle).forEach(([key, item]) => {
7363
+ if (item.type === "asset") {
7364
+ const moduleInfo = this.getModuleInfo(item.fileName);
7365
+ if (moduleInfo?.meta.originalId) {
7366
+ const meta = moduleInfo.meta;
7367
+ item.fileName = meta.originalId;
7368
+ if (meta.phpMapping) {
7369
+ item.source = PHP_Code.unescape(
7370
+ item.source.toString(),
7371
+ meta.phpMapping
7372
+ );
7373
+ }
7374
+ item.source = fixAssetsInjection(
7375
+ item.source.toString()
7376
+ );
7377
+ }
7378
+ } else if (item.type === "chunk" && item.facadeModuleId) {
7379
+ const moduleInfo = this.getModuleInfo(item.facadeModuleId);
7380
+ if (moduleInfo) {
7381
+ const meta = moduleInfo.meta;
7382
+ if (meta.phpMapping) {
7383
+ item.code = PHP_Code.unescape(
7384
+ item.code,
7385
+ meta.phpMapping
7386
+ );
7387
+ }
7388
+ item.code = fixAssetsInjection(item.code);
7389
+ }
7390
+ }
7391
+ });
7392
+ }
7393
+ }
7394
+ };
7395
+
6981
7396
  function usePHP(cfg = {}) {
6982
- const {
6983
- binary = "php",
6984
- entry = "index.php",
6985
- rewriteUrl = (requestUrl) => requestUrl,
6986
- tempDir = ".php-tmp",
6987
- cleanup = {}
6988
- } = cfg;
6989
- const { dev: devCleanup = true } = cleanup;
6990
- phpServer.binary = binary;
6991
- let config = void 0;
6992
- let viteServer = void 0;
6993
- let entries = Array.isArray(entry) ? entry : [entry];
6994
- function getTempFileName(file) {
6995
- return `${tempDir}/${file}.html`;
6996
- }
6997
- function cleanupTemp() {
6998
- rmSync(tempDir, { recursive: true, force: true });
6999
- }
7000
- function onExit() {
7001
- if (config?.command === "serve") {
7002
- phpServer.stop();
7003
- devCleanup && cleanupTemp();
7004
- }
7005
- process.exit();
7006
- }
7007
- [
7008
- "exit",
7009
- "SIGINT",
7010
- "SIGUSR1",
7011
- "SIGUSR2",
7012
- "uncaughtException",
7013
- "SIGTERM"
7014
- ].forEach((eventType) => {
7015
- process.on(eventType, onExit.bind(null));
7016
- });
7397
+ const { entry = "index.php" } = cfg;
7398
+ PHP_Server.binary = cfg.binary ?? PHP_Server.binary;
7399
+ serve.rewriteUrl = cfg.rewriteUrl ?? serve.rewriteUrl;
7400
+ shared.entries = Array.isArray(entry) ? entry : [entry];
7401
+ shared.tempDir = cfg.tempDir ?? shared.tempDir;
7402
+ shared.devConfig = {
7403
+ cleanup: cfg.dev?.cleanup || shared.devConfig.cleanup,
7404
+ errorLevels: cfg.dev?.errorLevels || shared.devConfig.errorLevels
7405
+ };
7017
7406
  return [
7018
7407
  {
7019
- name: "prepare-php",
7408
+ name: "init-php",
7020
7409
  enforce: "post",
7021
- config(config2, env) {
7022
- const gitIgnoreFile = `${tempDir}/.gitignore`;
7023
- if (!existsSync(gitIgnoreFile)) {
7024
- writeFile(gitIgnoreFile, "*\n**/*.php.html");
7025
- }
7026
- entries = [
7410
+ config(config, env) {
7411
+ shared.entries = [
7027
7412
  ...new Set(
7028
- entries.flatMap(
7413
+ shared.entries.flatMap(
7029
7414
  (entry2) => fastGlob.globSync(entry2, {
7030
7415
  dot: true,
7031
7416
  onlyFiles: true,
7032
7417
  unique: true,
7033
7418
  ignore: [
7034
- tempDir,
7035
- config2.build?.outDir || "dist"
7419
+ shared.tempDir,
7420
+ config.build?.outDir || "dist"
7036
7421
  ]
7037
7422
  })
7038
7423
  )
7039
7424
  )
7040
7425
  ];
7041
- consoleHijack(entries);
7426
+ consoleHijack();
7042
7427
  return {
7043
7428
  build: {
7044
- rollupOptions: { input: entries }
7429
+ rollupOptions: { input: shared.entries }
7045
7430
  },
7046
- optimizeDeps: { entries }
7431
+ optimizeDeps: { entries: shared.entries }
7047
7432
  };
7048
7433
  },
7049
7434
  configResolved(_config) {
7050
- config = _config;
7435
+ shared.viteConfig = _config;
7051
7436
  }
7052
7437
  },
7053
- {
7054
- name: "serve-php",
7055
- apply: "serve",
7056
- enforce: "pre",
7057
- configResolved(_config) {
7058
- config = _config;
7059
- entries.forEach((entry2) => {
7060
- const outputFile = getTempFileName(entry2);
7061
- escapePHP({
7062
- inputFile: entry2,
7063
- config
7064
- }).write(outputFile);
7065
- });
7066
- },
7067
- configureServer(server) {
7068
- viteServer = server;
7069
- phpServer.start(viteServer?.config.root);
7070
- server.middlewares.use(async (req, res, next) => {
7071
- try {
7072
- if (req.url && ![
7073
- "/@vite",
7074
- "/@fs",
7075
- "/@id/__x00__",
7076
- "/node_modules"
7077
- ].some((path) => req.url.startsWith(path))) {
7078
- req.on("error", (error) => {
7079
- throw error;
7080
- });
7081
- res.on("error", (error) => {
7082
- throw error;
7083
- });
7084
- const url = new URL(req.url, "http://localhost");
7085
- if (config?.server.port) {
7086
- url.port = config.server.port.toString();
7087
- }
7088
- const requestUrl = url.pathname;
7089
- if (url.pathname.endsWith("/")) {
7090
- url.pathname += "index.php";
7091
- }
7092
- const routedUrl = rewriteUrl(url);
7093
- if (routedUrl) {
7094
- url.pathname = routedUrl.pathname;
7095
- url.search = routedUrl.search;
7096
- url.hash = routedUrl.hash;
7097
- }
7098
- const entryPathname = url.pathname.substring(1);
7099
- const entry2 = entries.find((file) => {
7100
- return file === entryPathname || file.substring(0, file.lastIndexOf(".")) === entryPathname;
7101
- });
7102
- if (entry2) {
7103
- const tempFile = getTempFileName(entry2);
7104
- if (existsSync(resolve(tempFile))) {
7105
- url.pathname = tempFile;
7106
- url.port = phpServer.port.toString();
7107
- url.searchParams.set(
7108
- internalParam,
7109
- new URLSearchParams({
7110
- REQUEST_URI: requestUrl,
7111
- PHP_SELF: "/" + entry2
7112
- }).toString()
7113
- );
7114
- const body = await new Promise(
7115
- (resolve2, reject) => {
7116
- let data = [];
7117
- req.on("data", (chunk) => {
7118
- data.push(chunk);
7119
- }).on("end", () => {
7120
- resolve2(Buffer.concat(data));
7121
- });
7122
- }
7123
- );
7124
- const phpResult = await new Promise(async (resolve2, reject) => {
7125
- const chunks = [];
7126
- let statusCode;
7127
- let incomingHeaders = {};
7128
- const request = http.request(
7129
- url.toString(),
7130
- {
7131
- method: req.method,
7132
- headers: {
7133
- ...req.headers,
7134
- "content-length": Buffer.byteLength(
7135
- body
7136
- )
7137
- }
7138
- },
7139
- (msg) => {
7140
- statusCode = msg.statusCode;
7141
- incomingHeaders = msg.headers;
7142
- msg.on("data", (data) => {
7143
- chunks.push(data);
7144
- });
7145
- }
7146
- ).on("error", (error) => {
7147
- reject(error);
7148
- }).on("close", () => {
7149
- const content = Buffer.concat(
7150
- chunks
7151
- ).toString("utf8");
7152
- resolve2({
7153
- statusCode,
7154
- headers: incomingHeaders,
7155
- content
7156
- });
7157
- });
7158
- request.write(body, (error) => {
7159
- if (error) {
7160
- reject(error);
7161
- }
7162
- });
7163
- request.end();
7164
- });
7165
- let out = phpResult.content;
7166
- if (phpResult.headers["content-type"]?.includes("html")) {
7167
- out = await server.transformIndexHtml(
7168
- requestUrl,
7169
- out,
7170
- "/" + entryPathname
7171
- );
7172
- }
7173
- res.writeHead(
7174
- phpResult.statusCode || 200,
7175
- phpResult.headers
7176
- ).end(out);
7177
- return;
7178
- }
7179
- }
7180
- }
7181
- } catch (error) {
7182
- console.error("Vite-PHP Error: " + error);
7183
- }
7184
- next();
7185
- });
7186
- },
7187
- async handleHotUpdate({ server, file }) {
7188
- const entry2 = entries.find(
7189
- (entryFile) => resolve(entryFile) === resolve(file)
7190
- );
7191
- if (entry2) {
7192
- const outputFile = getTempFileName(entry2);
7193
- escapePHP({
7194
- inputFile: entry2,
7195
- config
7196
- }).write(outputFile);
7197
- server.moduleGraph.invalidateAll();
7198
- }
7199
- if (entry2 || !file.startsWith(resolve(tempDir)) && file.includes(".php")) {
7200
- server.ws.send({
7201
- type: "full-reload"
7202
- });
7203
- }
7204
- }
7205
- },
7206
- {
7207
- name: "build-php",
7208
- apply: "build",
7209
- enforce: "pre",
7210
- resolveId(source, importer, options) {
7211
- if (entries.includes(source)) {
7212
- return {
7213
- // Rename ids because Vite transforms only .html files: https://github.com/vitejs/vite/blob/0cde495ebeb48bcfb5961784a30bfaed997790a0/packages/vite/src/node/plugins/html.ts#L330
7214
- id: `${source}.html`,
7215
- resolvedBy: "vite-plugin-php",
7216
- meta: {
7217
- originalId: source
7218
- }
7219
- };
7220
- }
7221
- },
7222
- load(id, options) {
7223
- const entry2 = this.getModuleInfo(id)?.meta.originalId;
7224
- if (entry2) {
7225
- const { escapedCode, phpCodes } = escapePHP({
7226
- inputFile: entry2,
7227
- config
7228
- });
7229
- return {
7230
- code: escapedCode,
7231
- meta: { phpCodes }
7232
- };
7233
- }
7234
- },
7235
- generateBundle: {
7236
- order: "post",
7237
- handler(options, bundle, isWrite) {
7238
- Object.entries(bundle).forEach(([key, item]) => {
7239
- if (item.type === "asset") {
7240
- const meta = this.getModuleInfo(
7241
- item.fileName
7242
- )?.meta;
7243
- if (meta?.originalId && meta?.phpCodes) {
7244
- item.fileName = meta.originalId;
7245
- item.source = unescapePHP({
7246
- escapedCode: item.source.toString(),
7247
- phpCodes: meta.phpCodes
7248
- });
7249
- item.source = processOutput(item.source);
7250
- }
7251
- } else if (item.type === "chunk" && item.facadeModuleId) {
7252
- const meta = this.getModuleInfo(
7253
- item.facadeModuleId
7254
- )?.meta;
7255
- if (meta?.phpCodes) {
7256
- item.code = unescapePHP({
7257
- escapedCode: item.code,
7258
- phpCodes: meta.phpCodes
7259
- });
7260
- item.code = processOutput(item.code);
7261
- }
7262
- }
7263
- });
7264
- }
7265
- }
7266
- }
7438
+ servePlugin,
7439
+ buildPlugin
7267
7440
  ];
7268
7441
  }
7269
7442
 
7270
- export { usePHP as default };
7443
+ export { EPHPError, usePHP as default };