idfk
This commit is contained in:
@@ -27,6 +27,19 @@ if (!sessionId && !banned) {
|
||||
banned = true;
|
||||
}
|
||||
|
||||
if (!session_data && BoxId) {
|
||||
console.log("Missing session_data for BoxId %s", BoxId);
|
||||
}
|
||||
|
||||
let registered = false;
|
||||
let username = '';
|
||||
if (session_data) {
|
||||
registered = session_data.isRegistered();
|
||||
if (registered) {
|
||||
username = session_data.getSessionData("subscriber_username") || '';
|
||||
}
|
||||
}
|
||||
|
||||
// Current UTC time
|
||||
const now = new Date();
|
||||
|
||||
@@ -58,11 +71,10 @@ const {
|
||||
daylightOffset
|
||||
} = timezoneMap["UTC"];
|
||||
|
||||
ssid_sessions[socket.ssid] = new WTVClientSessionData(minisrv_config, socket.ssid);
|
||||
ssid_sessions[socket.ssid].set('SessionID', sessionId);
|
||||
|
||||
// Set session cookie on the client
|
||||
setCookie('SessionID', sessionId, { path: '/' });
|
||||
if (sessionId) {
|
||||
setCookie('SessionID', sessionId, { path: '/' });
|
||||
}
|
||||
|
||||
headers = `200 OK
|
||||
Content-type: text/html`
|
||||
@@ -73,9 +85,12 @@ data = `<html>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="checkmail" style="display:none"></iframe>
|
||||
<script src="msntv:/Javascript/TVShell.js" language="javascript"></script>
|
||||
<script src="msntv:/Javascript/ServiceList.js" language="javascript"></script>
|
||||
<script src="msntv:/Javascript/GuestUser.js" language="javascript"></script>
|
||||
<script language="javascript">
|
||||
try {
|
||||
var tvShell = new ActiveXObject("MSNTV.TVShell");
|
||||
var TVShell = new ActiveXObject("MSNTV.TVShell");
|
||||
var sink = new ActiveXObject("MSNTV.MultipleEventSink");
|
||||
|
||||
function getIDCRLCode(value) {
|
||||
@@ -85,42 +100,81 @@ data = `<html>
|
||||
function isIDCRLErrorCode(value) {
|
||||
return (value >>> 16) != 0;
|
||||
}
|
||||
var email = tvShell.UserManager.EMail;
|
||||
var wanProvider = tvShell.ConnectionManager.WANProvider;
|
||||
var email = TVShell.UserManager.EMail;
|
||||
var wanProvider = TVShell.ConnectionManager.WANProvider;
|
||||
|
||||
var banned = ${banned}; // JavaScript boolean value
|
||||
var registered = ${registered}; // JavaScript boolean value
|
||||
var username = "${username}"; // JavaScript string value
|
||||
|
||||
InitializeGuestMode();
|
||||
RemoveGuestUsers();
|
||||
|
||||
if (!banned) {
|
||||
var BuiltinServiceList = tvShell.BuiltinServiceList;
|
||||
var entry = BuiltinServiceList.Add("connection::login");
|
||||
entry.URL = "https://headwaiter.trusted.msntv.msn.com/connection/boxcheck.html?BoxId=${BoxId}";
|
||||
TVShell.AddSecretCode(10000); // sync shit
|
||||
TVShell.AddSecretCode(10001); // sync shit
|
||||
TVShell.AddSecretCode(10002); // sync shit
|
||||
TVShell.AddSecretCode(93288); // Service Select
|
||||
TVShell.AddSecretCode(77437); // Spooky Options
|
||||
TVShell.AddSecretCode(6145539); // Force Crash
|
||||
var entry = TVShell.ServiceList.Add("connection::login");
|
||||
entry.URL = "https://headwaiter.trusted.msntv.msn.com/connection/login.aspx?BoxId=${BoxId}";
|
||||
}
|
||||
|
||||
function DoLogin() {
|
||||
var currentUser = tvShell.UserManager.CurrentUser;
|
||||
|
||||
function CheckForUser(usernameToCheck) {
|
||||
var UserManager = TVShell.UserManager;
|
||||
for (var i=0; i<UserManager.Count; i++) {
|
||||
var user = UserManager.Item(i);
|
||||
if (user == usernameToCheck) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function DoLogin() {
|
||||
var currentUser = TVShell.UserManager.CurrentUser;
|
||||
if (currentUser == null) {
|
||||
if (banned === true) {
|
||||
var url = 'https://sg1.trusted.msntv.msn.com/connection/banned.html';
|
||||
var myPanel = tvShell.PanelManager.Item('main');
|
||||
var myPanel = TVShell.PanelManager.Item('main');
|
||||
if (myPanel) myPanel.GotoURL(url);
|
||||
} else {
|
||||
if (registered === true) {
|
||||
if (!CheckForUser(username)) {
|
||||
var user = TVShell.UserManager.AddNew(username);
|
||||
if (user) {
|
||||
user.IsPersistent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
SetProgress('Welcome, New User!', 100);
|
||||
tvShell.AddSecretCode(10000);
|
||||
tvShell.AddSecretCode(10001);
|
||||
tvShell.AddSecretCode(10002);
|
||||
tvShell.AddSecretCode(93288);
|
||||
tvShell.AddSecretCode(77437);
|
||||
tvShell.AddSecretCode(6145539);
|
||||
var myPanel = tvShell.PanelManager.Item('main');
|
||||
if (myPanel) myPanel.GotoURL('https://sg1.trusted.msntv.msn.com/register/Establish-your-MSN-TV-Account.html');
|
||||
tvShell.PanelManager.Item('main').ClearTravelLog();
|
||||
tvShell.PanelManager.Item('main').NoBackToMe = true;
|
||||
var myPanel = TVShell.PanelManager.Item('main')
|
||||
if (registered === true) {
|
||||
var signon = TVShell.BuiltinServiceList.Item("SignOn");
|
||||
var panel = TVShell.PanelManager.FocusedPanel;
|
||||
var atLogin = false;
|
||||
if ( signon && panel && panel.Name == "main" )
|
||||
{
|
||||
if ( IsMainPanelOnPage( signon.URL ) ) atLogin = true;
|
||||
}
|
||||
if (!atLogin) {
|
||||
myPanel.ClearTravelLog();
|
||||
myPanel.NoBackToMe = true;
|
||||
GotoSignOn();
|
||||
}
|
||||
} else {
|
||||
if (myPanel) myPanel.GotoURL('https://sg1.trusted.msntv.msn.com/register/Establish-your-MSN-TV-Account.html');
|
||||
}
|
||||
if (myPanel) {
|
||||
myPanel.ClearTravelLog();
|
||||
myPanel.NoBackToMe = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (banned === true) {
|
||||
var url = 'https://sg1.trusted.msntv.msn.com/connection/banned.html';
|
||||
var myPanel = tvShell.PanelManager.Item('service');
|
||||
var myPanel = TVShell.PanelManager.Item('service');
|
||||
if (myPanel) myPanel.GotoURL(url);
|
||||
}
|
||||
}
|
||||
@@ -138,14 +192,9 @@ data = `<html>
|
||||
serviceArgs[0] = ProductionArgs;
|
||||
serviceArgs[1] = PPEArgs;
|
||||
serviceArgs[2] = INTArgs;
|
||||
tvShell.AddSecretCode(10000);
|
||||
tvShell.AddSecretCode(10001);
|
||||
tvShell.AddSecretCode(10002);
|
||||
tvShell.AddSecretCode(93288);
|
||||
tvShell.AddSecretCode(6145539);
|
||||
try {
|
||||
tvShell.LoginManager.IDCRLInitialize(0);
|
||||
tvShell.LoginManager.IDCRLLogonAndAuthToServices(serviceArgs[0]);
|
||||
TVShell.LoginManager.IDCRLInitialize(0);
|
||||
TVShell.LoginManager.IDCRLLogonAndAuthToServices(serviceArgs[0]);
|
||||
} catch (e) {
|
||||
if (window.console) console.log("IDCRL error: " + e.message);
|
||||
}
|
||||
@@ -158,13 +207,13 @@ data = `<html>
|
||||
if (wanProvider === "MSNIANB") {
|
||||
var connector = GetConnectorByName("LocalPOP");
|
||||
if (connector == null) {
|
||||
connector = tvShell.ConnectionManager.MSNIAManager.Connectors.Add("modem");
|
||||
connector = TVShell.ConnectionManager.MSNIAManager.Connectors.Add("modem");
|
||||
connector.AreaCode = "";
|
||||
connector.Exchange = "";
|
||||
connector.DialingFlags = 0x00001000;
|
||||
connector.Name = "LocalPOP";
|
||||
connector.LocationName = "LocalPOP";
|
||||
tvShell.ConnectionManager.Save();
|
||||
TVShell.ConnectionManager.Save();
|
||||
connector.Poptimize("0", connector.AreaCode, connector.Exchange);
|
||||
}
|
||||
|
||||
@@ -177,7 +226,7 @@ data = `<html>
|
||||
}
|
||||
|
||||
function GetConnectorByName(name) {
|
||||
var connectors = tvShell.ConnectionManager.MSNIAManager.Connectors;
|
||||
var connectors = TVShell.ConnectionManager.MSNIAManager.Connectors;
|
||||
for (var i = 0; i < connectors.length; i++) {
|
||||
if (connectors[i].Name === name) {
|
||||
return connectors[i];
|
||||
@@ -187,13 +236,17 @@ data = `<html>
|
||||
}
|
||||
|
||||
function CheckBoxID() {
|
||||
SetProgress('minisrv/sg1 [0.0.0.1] Welcome, Guest!', 20);
|
||||
SetProgress("${minisrv_config.config.service_name} [${minisrv_config.config.hide_minisrv_version ? "beta" : minisrv_version_string.replace("zefie's wtv minisrv ","")}] Welcome, ${username != '' ? username : 'Guest'}!", 20);
|
||||
}
|
||||
|
||||
function GoToUserCheck() {
|
||||
var url = banned ? 'https://headwaiter.trusted.msntv.msn.com/connection/banned.html' : 'https://headwaiter.trusted.msntv.msn.com/connection/login.aspx';
|
||||
var myPanel = tvShell.PanelManager.Item('service');
|
||||
if (myPanel) myPanel.GotoURL(url);
|
||||
if (banned === true) {
|
||||
var url = 'https://headwaiter.trusted.msntv.msn.com/connection/banned.html';
|
||||
var myPanel = TVShell.PanelManager.Item('service');
|
||||
if (myPanel) myPanel.GotoURL(url);
|
||||
} else if (registered) {
|
||||
GotoSignOn();
|
||||
}
|
||||
}
|
||||
|
||||
function SetProgress(text, percent) {
|
||||
@@ -203,7 +256,7 @@ data = `<html>
|
||||
}
|
||||
}
|
||||
|
||||
var progressPanel = tvShell.PanelManager.Item('progress');
|
||||
var progressPanel = TVShell.PanelManager.Item('progress');
|
||||
|
||||
function IsServicePanel() {
|
||||
if ((window.name == null) || ((window.name != null) && (window.name.toLowerCase() != 'service'))) {
|
||||
@@ -213,11 +266,11 @@ data = `<html>
|
||||
}
|
||||
|
||||
function DontContinue() {
|
||||
var currentUser = tvShell.UserManager.CurrentUser;
|
||||
var currentUser = TVShell.UserManager.CurrentUser;
|
||||
if (currentUser != null && currentUser.IsAuthorized) {
|
||||
window.location.replace(tvShell.UserManager.CurrentUser.ServiceList.Item('home::home').URL);
|
||||
window.location.replace(TVShell.UserManager.CurrentUser.ServiceList.Item('home::home').URL);
|
||||
} else {
|
||||
tvShell.ConnectionManager.ServiceState = 'ReSignIn';
|
||||
TVShell.ConnectionManager.ServiceState = 'ReSignIn';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,14 +282,14 @@ data = `<html>
|
||||
DoLogin();
|
||||
|
||||
try {
|
||||
tvShell.DeviceControl.SetTimeZone(${standardOffset}, "${standardName}", 0, "");
|
||||
TVShell.DeviceControl.SetTimeZone(${standardOffset}, "${standardName}", 0, "");
|
||||
} catch (e) {
|
||||
if (window.console) console.log("SetTimeZone error: " + e.message);
|
||||
}
|
||||
|
||||
try {
|
||||
tvShell.DeviceControl.SetClock(${timeData.hh}, ${timeData.mm}, ${timeData.ss}, ${timeData.mo}, ${timeData.dd}, ${timeData.yyyy});
|
||||
tvShell.DeviceControl.ClockSet = true;
|
||||
TVShell.DeviceControl.SetClock(${timeData.hh}, ${timeData.mm}, ${timeData.ss}, ${timeData.mo}, ${timeData.dd}, ${timeData.yyyy});
|
||||
TVShell.DeviceControl.ClockSet = true;
|
||||
} catch (e) {
|
||||
if (window.console) console.log("SetClock error: " + e.message);
|
||||
}
|
||||
@@ -245,8 +298,8 @@ data = `<html>
|
||||
} catch (e) {
|
||||
if (window.console) console.log("Error in boxcheck: " + e.message);
|
||||
|
||||
var myPanel = tvShell ? tvShell.PanelManager.Item('main') : null;
|
||||
if (myPanel) myPanel.GotoURL('https://sg1.trusted.msntv.msn.com/connection/error.html');
|
||||
var myPanel = TVShell ? TVShell.PanelManager.Item('main') : null;
|
||||
if (myPanel) myPanel.GotoURL('https://headwaiter.trusted.msntv.msn.com/connection/error.html');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -4,10 +4,23 @@ const minisrv_service_file = true;
|
||||
|
||||
headers = `Content-type: text/html`;
|
||||
|
||||
if ( session_data && session_data.isRegistered() ) {
|
||||
data = `<html>
|
||||
<head>
|
||||
<title id="title"></title>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="checkmail" style="display:none"></iframe>
|
||||
<script language="javascript">
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
} else {
|
||||
data = `<html>
|
||||
<head>
|
||||
<title id="title"></title>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="checkmail" style="display:none"></iframe>
|
||||
<script language="javascript">
|
||||
@@ -29,4 +42,5 @@ data = `<html>
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
</html>`;
|
||||
}
|
||||
@@ -2,13 +2,14 @@ const minisrv_service_file = true;
|
||||
let promoCode = request_headers.query.promo_code || '';
|
||||
if (Array.isArray(promoCode)) promoCode = promoCode[0];
|
||||
if (promoCode) {
|
||||
if (minisrv_config.services['msntv2'] && minisrv_config.services['msntv2'].validPromoCodes && minisrv_config.services['msntv2'].validPromoCodes.includes(promoCode)) {
|
||||
if (minisrv_config.services[service_name] && minisrv_config.services[service_name].validPromoCodes && minisrv_config.services[service_name].validPromoCodes.includes(promoCode)) {
|
||||
console.log('Valid promo code entered: %s', promoCode);
|
||||
} else {
|
||||
console.warn('Invalid promo code entered: %s', promoCode);
|
||||
promoCode = '';
|
||||
}
|
||||
setCookie('promo_code', promoCode, { path: '/' });
|
||||
session_data.set('promo_code', promoCode);
|
||||
}
|
||||
|
||||
headers = `Status: 200 OK
|
||||
|
||||
@@ -82,7 +82,12 @@ data = `<HTML xmlns:msntv>
|
||||
<td style="margin: 0; padding: 0; vertical-align: middle; top: 2px; position: relative;"><img src="msntv:/Shared/Images/BulletCustom.gif" height="14" width="7" alt="Bullet"></td>
|
||||
<td style="margin: 0; padding: 0; width: 4px;"></td>
|
||||
<td style="margin: 0; padding: 0; font:bold 18; line-height: 20px;"><a class="shrLnk2" onclick="handleGuest()" style="display: inline-block; line-height: 20px;">I want to sign in as a guest</a></td>
|
||||
</tr>
|
||||
</tr>
|
||||
<tr style="margin: 0; padding: 0; top: 2px; position: relative;">
|
||||
<td style="margin: 0; padding: 0; vertical-align: middle; top: 2px; position: relative;"><img src="msntv:/Shared/Images/BulletCustom.gif" height="14" width="7" alt="Bullet"></td>
|
||||
<td style="margin: 0; padding: 0; width: 4px;"></td>
|
||||
<td style="margin: 0; padding: 0; font:bold 18; line-height: 20px;"><a class="shrLnk2" href="https://headwaiter.trusted.msntv.msn.com/connection/boxcheck.html" style="display: inline-block; line-height: 20px;">I want to start over</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</DIV>
|
||||
|
||||
@@ -1447,6 +1447,9 @@ class WTVMSNTV2 {
|
||||
|
||||
sendLocalFile(socket, filepath, request_headers) {
|
||||
this._populateQuery(request_headers);
|
||||
if (!request_headers.service_file_path) {
|
||||
request_headers.service_file_path = filepath;
|
||||
}
|
||||
try {
|
||||
const ext = path.extname(filepath).slice(1).toLowerCase();
|
||||
const serviceVaultDir = this.service_config.servicevault_dir || this.service_name;
|
||||
@@ -1466,8 +1469,8 @@ class WTVMSNTV2 {
|
||||
// Resolve socket.ssid from query params before running the script.
|
||||
// Priority: BoxID (direct SSID) > SessionID (looked up in ssid_sessions).
|
||||
if (socket.ssid === null && this.ssid_sessions) {
|
||||
const boxId = request_headers.query.BoxID || request_headers.query.boxid || null;
|
||||
const sessionId = request_headers.query.SessionID || request_headers.query.sessionid || null;
|
||||
const boxId = request_headers.query[this.wtvshared.getCaseInsensitiveKey('boxid', request_headers.query)] || null;
|
||||
const sessionId = request_headers.query[this.wtvshared.getCaseInsensitiveKey('sessionid', request_headers.query)] || null;
|
||||
if (boxId) {
|
||||
socket.ssid = this.wtvshared.makeSafeSSID(boxId);
|
||||
} else if (sessionId) {
|
||||
@@ -1485,7 +1488,8 @@ class WTVMSNTV2 {
|
||||
}
|
||||
if (socket.ssid && !this.ssid_sessions[socket.ssid]) {
|
||||
this.ssid_sessions[socket.ssid] = new this.WTVClientSessionData(this.minisrv_config, socket.ssid);
|
||||
this.ssid_sessions[socket.ssid].SaveIfRegistered();
|
||||
this.ssid_sessions[socket.ssid].switchUserID(0);
|
||||
this.ssid_sessions[socket.ssid].loadSessionData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1500,7 +1504,6 @@ class WTVMSNTV2 {
|
||||
request_is_async: false,
|
||||
session_data: (socket.ssid && this.ssid_sessions) ? this.ssid_sessions[socket.ssid] : null,
|
||||
ssid_sessions: this.ssid_sessions,
|
||||
WTVClientSessionData: this.WTVClientSessionData,
|
||||
// Scripts may call sendToClient directly for async mode;
|
||||
// wrap it so the response goes through SSLv2 encryption.
|
||||
sendToClient: (sock, hdrs, dat) => self._sendScriptResult(sock, request_headers, hdrs, dat),
|
||||
@@ -1588,6 +1591,10 @@ class WTVMSNTV2 {
|
||||
const responseHeaders = [];
|
||||
responseHeaders.push('HTTP/1.1 200 OK');
|
||||
responseHeaders.push(`Content-Type: ${contentType}`);
|
||||
const lastModified = this.wtvshared.getFileLastModifiedUTCString(filepath);
|
||||
if (lastModified) {
|
||||
responseHeaders.push(`Last-Modified: ${lastModified}`);
|
||||
}
|
||||
responseHeaders.push(`Content-Length: ${fileContents.length}`);
|
||||
const closeClientConnection = this._shouldCloseClientConnection(request_headers);
|
||||
responseHeaders.push(`Connection: ${closeClientConnection ? 'close' : 'Keep-Alive'}`);
|
||||
@@ -1663,6 +1670,14 @@ class WTVMSNTV2 {
|
||||
if (!headerLines.some(l => l.toLowerCase().startsWith('content-length'))) {
|
||||
headerLines.push(`Content-Length: ${body.length}`);
|
||||
}
|
||||
|
||||
if (!headerLines.some(l => l.toLowerCase().startsWith('last-modified')) && !headerLines.some(l => l.toLowerCase().startsWith('minisrv-no-last-modified')) && request_headers.service_file_path) {
|
||||
const lastModified = this.wtvshared.getFileLastModifiedUTCString(request_headers.service_file_path);
|
||||
if (lastModified) {
|
||||
headerLines.push(`Last-Modified: ${lastModified}`);
|
||||
}
|
||||
}
|
||||
|
||||
const closeClientConnection = this._shouldCloseClientConnection(request_headers, headerLines);
|
||||
if (!headerLines.some(l => l.toLowerCase().startsWith('connection:'))) {
|
||||
headerLines.push(`Connection: ${closeClientConnection ? 'close' : 'Keep-Alive'}`);
|
||||
|
||||
@@ -798,7 +798,7 @@ class WTVClientSessionData {
|
||||
|
||||
isRegistered(session_mode = true) {
|
||||
if (session_mode)
|
||||
return (this.getSessionData("registered") && this.fs.existsSync(this.getUserStoreDirectory()));
|
||||
return Boolean(this.getSessionData("registered") && this.fs.existsSync(this.getUserStoreDirectory()));
|
||||
else
|
||||
return this.fs.existsSync(this.getUserStoreDirectory());
|
||||
}
|
||||
|
||||
621
zefie_wtvp_minisrv/tools/service_script_check.js
Normal file
621
zefie_wtvp_minisrv/tools/service_script_check.js
Normal file
@@ -0,0 +1,621 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const vm = require('vm');
|
||||
|
||||
function printUsage() {
|
||||
console.error('Usage: node tools/service_script_check.js <script-file> [--line <n>] [--context <n>] [--script]');
|
||||
console.error('Examples:');
|
||||
console.error(' node tools/service_script_check.js boxcheck.html.js --line 83');
|
||||
console.error(' node tools/service_script_check.js boxcheck.html.js --script');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 2) {
|
||||
printUsage();
|
||||
}
|
||||
|
||||
let filePath;
|
||||
let lineNumber = null;
|
||||
let context = 5;
|
||||
let scriptMode = false;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg === '--line' || arg === '-l') {
|
||||
i += 1;
|
||||
lineNumber = args[i] ? Number(args[i]) : NaN;
|
||||
if (!Number.isInteger(lineNumber) || lineNumber < 1) {
|
||||
console.error('Error: --line must be a positive integer.');
|
||||
process.exit(1);
|
||||
}
|
||||
} else if (arg === '--context' || arg === '-c') {
|
||||
i += 1;
|
||||
context = args[i] ? Number(args[i]) : NaN;
|
||||
if (!Number.isInteger(context) || context < 0) {
|
||||
console.error('Error: --context must be a non-negative integer.');
|
||||
process.exit(1);
|
||||
}
|
||||
} else if (arg === '--script' || arg === '-s') {
|
||||
scriptMode = true;
|
||||
} else if (!filePath) {
|
||||
filePath = arg;
|
||||
} else {
|
||||
console.error(`Unknown argument: ${arg}`);
|
||||
printUsage();
|
||||
}
|
||||
}
|
||||
|
||||
if (!filePath || (!scriptMode && !lineNumber)) {
|
||||
printUsage();
|
||||
}
|
||||
|
||||
const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
|
||||
if (!fs.existsSync(absolutePath)) {
|
||||
console.error(`Error: file not found: ${absolutePath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const fileText = fs.readFileSync(absolutePath, 'utf8');
|
||||
|
||||
function findDataLiteral(text) {
|
||||
const dataPattern = /\bdata\b\s*=\s*/g;
|
||||
let match;
|
||||
while ((match = dataPattern.exec(text)) !== null) {
|
||||
let idx = match.index + match[0].length;
|
||||
while (idx < text.length && /\s/.test(text[idx])) idx += 1;
|
||||
if (idx >= text.length) break;
|
||||
const opener = text[idx];
|
||||
if (opener === '`' || opener === '"' || opener === "'") {
|
||||
return parseLiteral(text, idx);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseLiteral(text, startIndex) {
|
||||
const quote = text[startIndex];
|
||||
let value = '';
|
||||
let i = startIndex + 1;
|
||||
let escaped = false;
|
||||
let braceDepth = 0;
|
||||
let inExpression = false;
|
||||
|
||||
if (quote === '`') {
|
||||
while (i < text.length) {
|
||||
const ch = text[i];
|
||||
if (escaped) {
|
||||
value += ch;
|
||||
escaped = false;
|
||||
} else if (ch === '\\') {
|
||||
escaped = true;
|
||||
value += ch;
|
||||
} else if (ch === '$' && text[i + 1] === '{') {
|
||||
inExpression = true;
|
||||
braceDepth = 0;
|
||||
value += '${';
|
||||
i += 1;
|
||||
} else if (inExpression) {
|
||||
if (ch === '{') {
|
||||
braceDepth += 1;
|
||||
} else if (ch === '}') {
|
||||
if (braceDepth === 0) {
|
||||
inExpression = false;
|
||||
} else {
|
||||
braceDepth -= 1;
|
||||
}
|
||||
}
|
||||
value += ch;
|
||||
} else if (ch === '`') {
|
||||
return { text: value, startIndex, endIndex: i + 1 };
|
||||
} else {
|
||||
value += ch;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
while (i < text.length) {
|
||||
const ch = text[i];
|
||||
if (escaped) {
|
||||
value += ch;
|
||||
escaped = false;
|
||||
} else if (ch === '\\') {
|
||||
escaped = true;
|
||||
} else if (ch === quote) {
|
||||
return { text: value, startIndex, endIndex: i + 1 };
|
||||
} else {
|
||||
value += ch;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findScriptBlocks(html) {
|
||||
const pattern = /<script\b[^>]*>([\s\S]*?)<\/script>/gi;
|
||||
const blocks = [];
|
||||
let match;
|
||||
while ((match = pattern.exec(html)) !== null) {
|
||||
const contentOffset = match[0].indexOf(match[1]);
|
||||
blocks.push({
|
||||
content: match[1],
|
||||
startIndex: match.index,
|
||||
contentStartIndex: match.index + contentOffset,
|
||||
raw: match[0]
|
||||
});
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
function computeLineNumber(text, index) {
|
||||
return text.slice(0, index).split(/\r\n|\n|\r/).length + 1;
|
||||
}
|
||||
|
||||
function printContext(lines, errorLine, context) {
|
||||
const startLine = Math.max(1, errorLine - context);
|
||||
const endLine = Math.min(lines.length, errorLine + context);
|
||||
const width = String(endLine).length;
|
||||
for (let idx = startLine; idx <= endLine; idx += 1) {
|
||||
const marker = idx === errorLine ? '>' : ' ';
|
||||
const padded = String(idx).padStart(width, ' ');
|
||||
console.log(`${marker} ${padded}: ${lines[idx - 1]}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getErrorLocation(err) {
|
||||
const lineNumber = err.lineNumber || err.line || err.loc?.line || null;
|
||||
const columnNumber = err.columnNumber || err.column || err.loc?.column || null;
|
||||
if (lineNumber) {
|
||||
return { lineNumber, columnNumber };
|
||||
}
|
||||
|
||||
if (typeof err.stack === 'string') {
|
||||
const match = err.stack.match(/\n\s*at .*:(\d+):(\d+)/);
|
||||
if (match) {
|
||||
return { lineNumber: Number(match[1]), columnNumber: Number(match[2]) };
|
||||
}
|
||||
}
|
||||
return { lineNumber: null, columnNumber: null };
|
||||
}
|
||||
|
||||
function scanBraceMismatches(text) {
|
||||
let line = 1;
|
||||
let column = 1;
|
||||
let inSingle = false;
|
||||
let inDouble = false;
|
||||
let inBacktick = false;
|
||||
let escaped = false;
|
||||
let inLineComment = false;
|
||||
let inBlockComment = false;
|
||||
let templateExprDepth = 0;
|
||||
const unmatchedCloses = [];
|
||||
const openStack = [];
|
||||
|
||||
for (let i = 0; i < text.length; i += 1) {
|
||||
const ch = text[i];
|
||||
const next = text[i + 1];
|
||||
|
||||
if (inLineComment) {
|
||||
if (ch === '\n') {
|
||||
inLineComment = false;
|
||||
line += 1;
|
||||
column = 1;
|
||||
} else {
|
||||
column += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inBlockComment) {
|
||||
if (ch === '*' && next === '/') {
|
||||
inBlockComment = false;
|
||||
i += 1;
|
||||
column += 2;
|
||||
continue;
|
||||
}
|
||||
if (ch === '\n') {
|
||||
line += 1;
|
||||
column = 1;
|
||||
} else {
|
||||
column += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inSingle) {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
} else if (ch === '\\') {
|
||||
escaped = true;
|
||||
} else if (ch === "'") {
|
||||
inSingle = false;
|
||||
}
|
||||
if (ch === '\n') {
|
||||
line += 1;
|
||||
column = 1;
|
||||
} else {
|
||||
column += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inDouble) {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
} else if (ch === '\\') {
|
||||
escaped = true;
|
||||
} else if (ch === '"') {
|
||||
inDouble = false;
|
||||
}
|
||||
if (ch === '\n') {
|
||||
line += 1;
|
||||
column = 1;
|
||||
} else {
|
||||
column += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inBacktick) {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
} else if (ch === '\\') {
|
||||
escaped = true;
|
||||
} else if (ch === '$' && next === '{') {
|
||||
templateExprDepth += 1;
|
||||
i += 1;
|
||||
column += 2;
|
||||
continue;
|
||||
} else if (ch === '`' && templateExprDepth === 0) {
|
||||
inBacktick = false;
|
||||
} else if (ch === '}' && templateExprDepth > 0) {
|
||||
templateExprDepth -= 1;
|
||||
}
|
||||
if (ch === '\n') {
|
||||
line += 1;
|
||||
column = 1;
|
||||
} else {
|
||||
column += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '/' && next === '/') {
|
||||
inLineComment = true;
|
||||
i += 1;
|
||||
column += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '/' && next === '*') {
|
||||
inBlockComment = true;
|
||||
i += 1;
|
||||
column += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === "'") {
|
||||
inSingle = true;
|
||||
column += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '"') {
|
||||
inDouble = true;
|
||||
column += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '`') {
|
||||
inBacktick = true;
|
||||
templateExprDepth = 0;
|
||||
column += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '{') {
|
||||
if (!inBacktick || templateExprDepth > 0) {
|
||||
openStack.push({ line, column });
|
||||
}
|
||||
column += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '}') {
|
||||
if (!inBacktick || templateExprDepth > 0) {
|
||||
if (openStack.length === 0) {
|
||||
unmatchedCloses.push({ line, column });
|
||||
} else {
|
||||
openStack.pop();
|
||||
}
|
||||
}
|
||||
column += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '\n') {
|
||||
line += 1;
|
||||
column = 1;
|
||||
} else {
|
||||
column += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return { unmatchedCloses, unmatchedOpens: openStack };
|
||||
}
|
||||
|
||||
function findUnmatchedCloseBrace(text) {
|
||||
const result = scanBraceMismatches(text);
|
||||
if (result.unmatchedCloses.length > 0) {
|
||||
return result.unmatchedCloses[0].line;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findLikelyExtraCloseBrace(text, errorLine) {
|
||||
const result = scanBraceMismatches(text);
|
||||
if (result.unmatchedCloses.length > 0) {
|
||||
return result.unmatchedCloses[0].line;
|
||||
}
|
||||
if (result.unmatchedOpens.length > 0) {
|
||||
// Unclosed open brace—return the last remaining open brace before the end.
|
||||
return result.unmatchedOpens[result.unmatchedOpens.length - 1].line;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findLikelyExtraCloseBrace(text, errorLine) {
|
||||
let line = 1;
|
||||
let inSingle = false;
|
||||
let inDouble = false;
|
||||
let inBacktick = false;
|
||||
let escaped = false;
|
||||
let inLineComment = false;
|
||||
let inBlockComment = false;
|
||||
let templateExprDepth = 0;
|
||||
const positions = [];
|
||||
|
||||
for (let i = 0; i < text.length; i += 1) {
|
||||
const ch = text[i];
|
||||
const next = text[i + 1];
|
||||
|
||||
if (inLineComment) {
|
||||
if (ch === '\n') {
|
||||
inLineComment = false;
|
||||
line += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inBlockComment) {
|
||||
if (ch === '*' && next === '/') {
|
||||
inBlockComment = false;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (ch === '\n') {
|
||||
line += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inSingle) {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
} else if (ch === '\\') {
|
||||
escaped = true;
|
||||
} else if (ch === "'") {
|
||||
inSingle = false;
|
||||
}
|
||||
if (ch === '\n') {
|
||||
line += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inDouble) {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
} else if (ch === '\\') {
|
||||
escaped = true;
|
||||
} else if (ch === '"') {
|
||||
inDouble = false;
|
||||
}
|
||||
if (ch === '\n') {
|
||||
line += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inBacktick) {
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
} else if (ch === '\\') {
|
||||
escaped = true;
|
||||
} else if (ch === '$' && next === '{') {
|
||||
templateExprDepth += 1;
|
||||
i += 1;
|
||||
continue;
|
||||
} else if (ch === '`' && templateExprDepth === 0) {
|
||||
inBacktick = false;
|
||||
} else if (ch === '}' && templateExprDepth > 0) {
|
||||
templateExprDepth -= 1;
|
||||
}
|
||||
if (ch === '\n') {
|
||||
line += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '/' && next === '/') {
|
||||
inLineComment = true;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '/' && next === '*') {
|
||||
inBlockComment = true;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === "'") {
|
||||
inSingle = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '"') {
|
||||
inDouble = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '`') {
|
||||
inBacktick = true;
|
||||
templateExprDepth = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '}') {
|
||||
positions.push(line);
|
||||
}
|
||||
|
||||
if (ch === '\n') {
|
||||
line += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = positions.length - 1; i >= 0; i -= 1) {
|
||||
if (positions[i] < errorLine) {
|
||||
return positions[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const literal = findDataLiteral(fileText);
|
||||
if (!literal) {
|
||||
console.error('Error: Could not locate a `data` assignment or recognizable string literal in the specified file.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const dataStartLine = computeLineNumber(fileText, literal.startIndex);
|
||||
const dataText = literal.text;
|
||||
const dataLines = dataText.split(/\r\n|\n|\r/);
|
||||
const totalLines = dataLines.length;
|
||||
|
||||
if (lineNumber !== null) {
|
||||
if (lineNumber > totalLines) {
|
||||
console.error(`Error: requested line ${lineNumber} is beyond data content length (${totalLines} lines).`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const startLine = Math.max(1, lineNumber - context);
|
||||
const endLine = Math.min(totalLines, lineNumber + context);
|
||||
const width = String(endLine).length;
|
||||
|
||||
console.log(`File: ${absolutePath}`);
|
||||
console.log(`Data content lines ${startLine}-${endLine} of ${totalLines} (requested ${lineNumber})`);
|
||||
for (let idx = startLine; idx <= endLine; idx += 1) {
|
||||
const marker = idx === lineNumber ? '>' : ' ';
|
||||
const padded = String(idx).padStart(width, ' ');
|
||||
console.log(`${marker} ${padded}: ${dataLines[idx - 1]}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (scriptMode) {
|
||||
const scriptBlocks = findScriptBlocks(dataText);
|
||||
if (scriptBlocks.length === 0) {
|
||||
console.error('Error: no <script> blocks found in data content.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Checking ${scriptBlocks.length} <script> block(s) inside data content for syntax errors...`);
|
||||
let errorCount = 0;
|
||||
|
||||
for (let idx = 0; idx < scriptBlocks.length; idx += 1) {
|
||||
const block = scriptBlocks[idx];
|
||||
const blockLine = computeLineNumber(dataText, block.contentStartIndex);
|
||||
const filename = `${absolutePath} [data script ${idx + 1}]`;
|
||||
try {
|
||||
new vm.Script(block.content, { filename });
|
||||
console.log(` [OK] script block ${idx + 1} starting at data line ${blockLine}`);
|
||||
} catch (err) {
|
||||
errorCount += 1;
|
||||
const location = getErrorLocation(err);
|
||||
const lineNumberInScript = location.lineNumber;
|
||||
const columnNumber = location.columnNumber;
|
||||
const braceResult = scanBraceMismatches(block.content);
|
||||
const fallbackLine = block.content.split(/\r\n|\n|\r/).length + 1;
|
||||
const mismatchLine = findLikelyExtraCloseBrace(block.content, lineNumberInScript || fallbackLine);
|
||||
let report = ` [ERROR] script block ${idx + 1} starting at data line ${blockLine}: ${err.message}`;
|
||||
let absoluteErrorLine = null;
|
||||
if (lineNumberInScript) {
|
||||
absoluteErrorLine = blockLine + lineNumberInScript - 1;
|
||||
const absoluteErrorFileLine = dataStartLine + absoluteErrorLine - 1;
|
||||
report += ` (script line ${lineNumberInScript}, data line ${absoluteErrorLine}, file line ${absoluteErrorFileLine}`;
|
||||
if (columnNumber) report += `, column ${columnNumber}`;
|
||||
report += `)`;
|
||||
}
|
||||
if (braceResult.unmatchedCloses.length > 0) {
|
||||
const close = braceResult.unmatchedCloses[0];
|
||||
const absoluteMismatchLine = blockLine + close.line - 1;
|
||||
const absoluteMismatchFileLine = dataStartLine + absoluteMismatchLine - 1;
|
||||
report += ` [unmatched '}' at script line ${close.line}, data line ${absoluteMismatchLine}, file line ${absoluteMismatchFileLine}]`;
|
||||
} else if (braceResult.unmatchedOpens.length > 0) {
|
||||
const open = braceResult.unmatchedOpens[braceResult.unmatchedOpens.length - 1];
|
||||
const absoluteMismatchLine = blockLine + open.line - 1;
|
||||
const absoluteMismatchFileLine = dataStartLine + absoluteMismatchLine - 1;
|
||||
report += ` [unclosed '{' at script line ${open.line}, data line ${absoluteMismatchLine}, file line ${absoluteMismatchFileLine}]`;
|
||||
} else if (mismatchLine) {
|
||||
const absoluteMismatchLine = blockLine + mismatchLine - 1;
|
||||
const absoluteMismatchFileLine = dataStartLine + absoluteMismatchLine - 1;
|
||||
report += ` [extra '}' likely at script line ${mismatchLine}, data line ${absoluteMismatchLine}, file line ${absoluteMismatchFileLine}]`;
|
||||
}
|
||||
console.error(report);
|
||||
|
||||
const blockLines = block.content.split(/\r\n|\n|\r/);
|
||||
if (braceResult.unmatchedCloses.length > 0) {
|
||||
const close = braceResult.unmatchedCloses[0];
|
||||
console.log(` [unmatched close brace] script line ${close.line}:`);
|
||||
const hintContextStart = Math.max(1, close.line - context);
|
||||
const hintContextEnd = Math.min(blockLines.length, close.line + context);
|
||||
for (let j = hintContextStart; j <= hintContextEnd; j += 1) {
|
||||
const marker = j === close.line ? '>' : ' ';
|
||||
const width = String(hintContextEnd).length;
|
||||
const padded = String(j).padStart(width, ' ');
|
||||
console.log(`${marker} ${padded}: ${blockLines[j - 1]}`);
|
||||
}
|
||||
} else if (braceResult.unmatchedOpens.length > 0) {
|
||||
const open = braceResult.unmatchedOpens[braceResult.unmatchedOpens.length - 1];
|
||||
console.log(` [unclosed open brace] script line ${open.line}:`);
|
||||
const hintContextStart = Math.max(1, open.line - context);
|
||||
const hintContextEnd = Math.min(blockLines.length, open.line + context);
|
||||
for (let j = hintContextStart; j <= hintContextEnd; j += 1) {
|
||||
const marker = j === open.line ? '>' : ' ';
|
||||
const width = String(hintContextEnd).length;
|
||||
const padded = String(j).padStart(width, ' ');
|
||||
console.log(`${marker} ${padded}: ${blockLines[j - 1]}`);
|
||||
}
|
||||
} else if (mismatchLine) {
|
||||
console.log(` [extra brace] script line ${mismatchLine} detected as the likely cause:`);
|
||||
const hintContextStart = Math.max(1, mismatchLine - context);
|
||||
const hintContextEnd = Math.min(blockLines.length, mismatchLine + context);
|
||||
for (let j = hintContextStart; j <= hintContextEnd; j += 1) {
|
||||
const marker = j === mismatchLine ? '>' : ' ';
|
||||
const width = String(hintContextEnd).length;
|
||||
const padded = String(j).padStart(width, ' ');
|
||||
console.log(`${marker} ${padded}: ${blockLines[j - 1]}`);
|
||||
}
|
||||
} else if (lineNumberInScript) {
|
||||
console.log(` Context around error in script block ${idx + 1}:`);
|
||||
printContext(blockLines, lineNumberInScript, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errorCount > 0) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user