New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

esbuild-plugin-scriptable

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

esbuild-plugin-scriptable - npm Package Compare versions

Comparing version 0.5.4 to 0.5.5

8

dist/index.js

@@ -1,5 +0,5 @@

export * from './scriptable-banner';
export * from './scriptable-deploy';
export * from './types';
export * from "./scriptable-banner.js";
export * from "./scriptable-deploy.js";
export * from "./types.js";
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map

@@ -1,113 +0,104 @@

import { generateScriptableBanner } from '@scriptables/manifest/generateScriptableBanner';
import { validateManifest } from '@scriptables/manifest/validateManifest';
import { existsSync } from 'fs';
import { readFile, writeFile } from 'fs/promises';
import { dirname, basename, resolve, posix, sep } from 'path';
import { createLogger } from './logger';
import { isMatch, normalizePath, waitForFile } from './utils';
import { generateScriptableBanner } from "@scriptables/manifest/generateScriptableBanner";
import { validateManifest } from "@scriptables/manifest/validateManifest";
import { existsSync } from "fs";
import { readFile, writeFile } from "fs/promises";
import { dirname, basename, resolve, posix, sep } from "path";
import { createLogger } from "./logger.js";
import { isMatch, normalizePath, waitForFile } from "./utils.js";
const DEFAULT_MANIFEST_EXTENSIONS = [".manifest.json", ".manifest", ".json"];
function scriptableBanner(options = {}) {
const {
manifestExtensions = DEFAULT_MANIFEST_EXTENSIONS,
warnOnMissingManifest = true,
warnOnInvalidManifest = true,
warnOnMissingEntry = true,
getManifestPath,
patterns = ["**/*.{ts,tsx,js,jsx}"]
} = options;
const logger = createLogger("scriptable-banner");
return {
name: "scriptable",
setup(build) {
let manifestCache = /* @__PURE__ */ new Map();
if (!build.initialOptions.metafile) {
logger.warn(
'Warning: "metafile" option was not enabled. Automatically setting "metafile: true" to allow scriptable plugin to modify output files.'
);
build.initialOptions.metafile = true;
}
build.onStart(() => {
manifestCache.clear();
});
build.onResolve({ filter: /.*/ }, async (args) => {
if (!isMatch(args.path, patterns)) {
return null;
}
if (Array.isArray(build.initialOptions.entryPoints) && build.initialOptions.entryPoints.map(String).includes(args.path)) {
const dir = dirname(args.path);
const baseFileName = basename(args.path).replace(/\.[tj]sx?$/, "");
const manifestPath = getManifestPath ? getManifestPath(args.path) : manifestExtensions.map((ext) => resolve(dir, `${baseFileName}${ext}`)).find(existsSync);
if (manifestPath) {
try {
const manifestContent = await readFile(manifestPath, "utf8");
const manifest = JSON.parse(manifestContent);
const normalizedPath = normalizePath(args.path);
try {
validateManifest(manifest);
} catch (error) {
if (warnOnInvalidManifest && error instanceof Error) {
logger.warn(`Invalid manifest file: ${manifestPath} - ${error.message}`, error);
const { manifestExtensions = DEFAULT_MANIFEST_EXTENSIONS, warnOnMissingManifest = true, warnOnInvalidManifest = true, warnOnMissingEntry = true, getManifestPath, patterns = ["**/*.{ts,tsx,js,jsx}"] } = options;
const logger = createLogger("scriptable-banner");
return {
name: "scriptable",
setup(build) {
let manifestCache = /* @__PURE__ */ new Map();
if (!build.initialOptions.metafile) {
logger.warn("Warning: \"metafile\" option was not enabled. Automatically setting \"metafile: true\" to allow scriptable plugin to modify output files.");
build.initialOptions.metafile = true;
}
build.onStart(() => {
manifestCache.clear();
});
build.onResolve({ filter: /.*/ }, async (args) => {
if (!isMatch(args.path, patterns)) {
return null;
}
if (Array.isArray(build.initialOptions.entryPoints) && build.initialOptions.entryPoints.map(String).includes(args.path)) {
const dir = dirname(args.path);
const baseFileName = basename(args.path).replace(/\.[tj]sx?$/, "");
const manifestPath = getManifestPath ? getManifestPath(args.path) : manifestExtensions.map((ext) => resolve(dir, `${baseFileName}${ext}`)).find(existsSync);
if (manifestPath) {
try {
const manifestContent = await readFile(manifestPath, "utf8");
const manifest = JSON.parse(manifestContent);
const normalizedPath = normalizePath(args.path);
try {
validateManifest(manifest);
}
catch (error) {
if (warnOnInvalidManifest && error instanceof Error) {
logger.warn(`Invalid manifest file: ${manifestPath} - ${error.message}`, error);
}
return null;
}
manifestCache.set(normalizedPath, manifest);
}
catch (error) {
logger.warn(`Failed to read manifest file: ${manifestPath}`, error);
}
}
else if (warnOnMissingManifest) {
logger.warn(`No manifest file found for entry point: ${args.path}`);
}
}
return null;
}
manifestCache.set(normalizedPath, manifest);
} catch (error) {
logger.warn(`Failed to read manifest file: ${manifestPath}`, error);
}
} else if (warnOnMissingManifest) {
logger.warn(`No manifest file found for entry point: ${args.path}`);
}
});
build.onEnd(async (result) => {
if (!result.metafile || Object.keys(result.metafile.outputs).length === 0) {
logger.warn("Skipping banner addition because \"metafile\" is not available or contains no outputs. Ensure \"metafile: true\" is set in esbuild options if write is enabled.");
return;
}
const outputs = result.metafile.outputs;
for (const outputPath of Object.keys(outputs)) {
const outputMeta = outputs[outputPath];
const entryPoint = outputMeta?.entryPoint ?? "";
const normalizedEntryPath = entryPoint ? posix.normalize(entryPoint.split(sep).join("/")) : "";
if (!normalizedEntryPath || !manifestCache.has(normalizedEntryPath)) {
if (warnOnMissingEntry) {
logger.warn(`No matching entry point found for output file: ${outputPath}`);
}
continue;
}
try {
const manifest = manifestCache.get(normalizedEntryPath) || {};
const banner = generateScriptableBanner(manifest);
if (result.outputFiles) {
const outputFile = result.outputFiles.find((file) => file.path === outputPath || file.path === "<stdout>");
if (outputFile) {
outputFile.contents = Buffer.from(banner + outputFile.text.trimEnd());
}
}
else {
try {
await waitForFile(outputPath);
const content = await readFile(outputPath, "utf-8");
const updatedContent = banner + content.trimEnd();
await writeFile(outputPath, updatedContent, "utf8");
}
catch (error) {
logger.error(`Failed to process file ${outputPath}: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
catch (error) {
logger.error(`Failed to process output file: ${outputPath}`, error);
}
}
});
}
return null;
});
build.onEnd(async (result) => {
if (!result.metafile || Object.keys(result.metafile.outputs).length === 0) {
logger.warn(
'Skipping banner addition because "metafile" is not available or contains no outputs. Ensure "metafile: true" is set in esbuild options if write is enabled.'
);
return;
}
const outputs = result.metafile.outputs;
for (const outputPath of Object.keys(outputs)) {
const outputMeta = outputs[outputPath];
const entryPoint = outputMeta?.entryPoint ?? "";
const normalizedEntryPath = entryPoint ? posix.normalize(entryPoint.split(sep).join("/")) : "";
if (!normalizedEntryPath || !manifestCache.has(normalizedEntryPath)) {
if (warnOnMissingEntry) {
logger.warn(`No matching entry point found for output file: ${outputPath}`);
}
continue;
}
try {
const manifest = manifestCache.get(normalizedEntryPath) || {};
const banner = generateScriptableBanner(manifest);
if (result.outputFiles) {
const outputFile = result.outputFiles.find((file) => file.path === outputPath || file.path === "<stdout>");
if (outputFile) {
outputFile.contents = Buffer.from(banner + outputFile.text.trimEnd());
}
} else {
try {
await waitForFile(outputPath);
const content = await readFile(outputPath, "utf-8");
const updatedContent = banner + content.trimEnd();
await writeFile(outputPath, updatedContent, "utf8");
} catch (error) {
logger.error(
`Failed to process file ${outputPath}: ${error instanceof Error ? error.message : String(error)}`
);
}
}
} catch (error) {
logger.error(`Failed to process output file: ${outputPath}`, error);
}
}
});
}
};
};
}
export { scriptableBanner };
//# sourceMappingURL=scriptable-banner.js.map
//# sourceMappingURL=scriptable-banner.js.map
//# sourceMappingURL=scriptable-banner.js.map

@@ -1,134 +0,130 @@

import { generateScriptableBanner } from '@scriptables/manifest/generateScriptableBanner';
import { findICloudDrivePaths, PathType } from 'findicloud';
import { readFile, writeFile } from 'fs/promises';
import { basename, join } from 'path';
import { createLogger } from './logger';
import { createPatternMatcher, isPathEqual, waitForFile } from './utils';
import { generateScriptableBanner } from "@scriptables/manifest/generateScriptableBanner";
import { findICloudDrivePaths, PathType } from "findicloud";
import { readFile, writeFile } from "fs/promises";
import { basename, join } from "path";
import { createLogger } from "./logger.js";
import { createPatternMatcher, isPathEqual, waitForFile } from "./utils.js";
const findICloudBugsUrl = "https://github.com/hexxspark/findicloud/issues";
async function tryReadManifest(filePath, extensions) {
for (const ext of extensions) {
try {
const manifestPath = filePath.replace(/\.[^.]+$/, "") + ext;
const content = await readFile(manifestPath, "utf-8");
return JSON.parse(content);
} catch {
continue;
for (const ext of extensions) {
try {
const manifestPath = filePath.replace(/\.[^.]+$/, "") + ext;
const content = await readFile(manifestPath, "utf-8");
return JSON.parse(content);
}
catch {
continue;
}
}
}
return null;
return null;
}
function scriptableDeploy(options = {}) {
const {
verbose = false,
continueOnError = true,
scriptableDir,
patterns = ["**/*.{ts,tsx,js,jsx}"],
addBanner = true,
manifestExtensions = [".manifest.json", ".manifest", ".json"]
} = options;
const filterFile = createPatternMatcher(patterns);
const logger = createLogger("scriptable-deploy", {
level: verbose ? "verbose" : "info"
});
let targetDir;
return {
name: "scriptable-deploy",
setup(build) {
if (!build.initialOptions.metafile) {
logger.verbose("Automatically enabling metafile option");
build.initialOptions.metafile = true;
}
build.onStart(async () => {
try {
if (scriptableDir) {
targetDir = scriptableDir;
} else {
const [found] = await findICloudDrivePaths({ appNamePattern: "Scriptable", types: [PathType.APP_STORAGE] });
targetDir = found?.path;
}
if (!targetDir) {
throw new Error(
`Scriptable directory not found in iCloud Drive. Please ensure iCloud Drive is installed and Scriptable app is synced with iCloud. If iCloud Drive is not installed, you can download it from: https://support.apple.com/en-us/HT204283. If the issue persists, please report it at: ${findICloudBugsUrl}`
);
}
logger.verbose(`Target directory: ${targetDir}`);
} catch (error) {
const message = `Failed to find Scriptable directory: ${error.message}`;
if (!continueOnError) {
throw new Error(message);
}
logger.error(message);
}
});
build.onEnd(async (result) => {
if (!result.metafile || !targetDir) {
logger.error("Missing required configuration");
return;
}
try {
const outputs = Object.keys(result.metafile.outputs).filter((file) => {
const { entryPoint } = result.metafile.outputs[file];
return entryPoint && filterFile(entryPoint);
});
for (const file of outputs) {
const output = result.metafile.outputs[file];
let content;
if (result.outputFiles) {
const outputFile = result.outputFiles.find((f) => isPathEqual(f.path, file));
if (!outputFile) {
throw new Error(`Output file not found: ${file}`);
}
content = new TextDecoder().decode(outputFile.contents);
} else {
await waitForFile(file);
content = await readFile(file, "utf-8");
const { verbose = false, continueOnError = true, scriptableDir, patterns = ["**/*.{ts,tsx,js,jsx}"], addBanner = true, manifestExtensions = [".manifest.json", ".manifest", ".json"] } = options;
const filterFile = createPatternMatcher(patterns);
const logger = createLogger("scriptable-deploy", {
level: verbose ? "verbose" : "info"
});
let targetDir;
return {
name: "scriptable-deploy",
setup(build) {
if (!build.initialOptions.metafile) {
logger.verbose("Automatically enabling metafile option");
build.initialOptions.metafile = true;
}
let targetFileName = basename(file);
let manifest = null;
try {
const inputs = Object.entries(output.inputs);
if (!inputs.length) {
throw new Error(`No input files found for ${file}`);
}
const sourceFile = inputs[0][0];
if (!sourceFile) {
throw new Error(`Invalid source file for ${file}`);
}
manifest = await tryReadManifest(sourceFile, manifestExtensions);
if (manifest?.name) {
targetFileName = `${manifest.name}.js`;
}
} catch (error) {
logger.warn(`Failed to read manifest for ${file}: ${error}`);
}
const targetPath = join(targetDir, targetFileName);
if (addBanner && manifest) {
try {
const banner = generateScriptableBanner(manifest);
const newContent = banner + "\n" + content;
await writeFile(targetPath, newContent);
logger.info(`Deployed with banner: ${file} -> ${targetPath}`);
continue;
} catch (error) {
logger.warn(`Failed to add banner to ${file}: ${error}`);
}
}
await writeFile(targetPath, content);
logger.info(`Deployed: ${file} -> ${targetPath}`);
}
logger.info(`Successfully deployed ${outputs.length} files`);
} catch (error) {
logger.error(`Deploy failed: ${error.message}`);
if (!continueOnError) {
throw error;
}
build.onStart(async () => {
try {
if (scriptableDir) {
targetDir = scriptableDir;
}
else {
const [found] = await findICloudDrivePaths({ appNamePattern: "Scriptable", types: [PathType.APP_STORAGE] });
targetDir = found?.path;
}
if (!targetDir) {
throw new Error(`Scriptable directory not found in iCloud Drive. Please ensure iCloud Drive is installed and Scriptable app is synced with iCloud. If iCloud Drive is not installed, you can download it from: https://support.apple.com/en-us/HT204283. If the issue persists, please report it at: ${findICloudBugsUrl}`);
}
logger.verbose(`Target directory: ${targetDir}`);
}
catch (error) {
const message = `Failed to find Scriptable directory: ${error.message}`;
if (!continueOnError) {
throw new Error(message);
}
logger.error(message);
}
});
build.onEnd(async (result) => {
if (!result.metafile || !targetDir) {
logger.error("Missing required configuration");
return;
}
try {
const outputs = Object.keys(result.metafile.outputs).filter((file) => {
const { entryPoint } = result.metafile.outputs[file];
return entryPoint && filterFile(entryPoint);
});
for (const file of outputs) {
const output = result.metafile.outputs[file];
let content;
if (result.outputFiles) {
const outputFile = result.outputFiles.find((f) => isPathEqual(f.path, file));
if (!outputFile) {
throw new Error(`Output file not found: ${file}`);
}
content = new TextDecoder().decode(outputFile.contents);
}
else {
await waitForFile(file);
content = await readFile(file, "utf-8");
}
let targetFileName = basename(file);
let manifest = null;
try {
const inputs = Object.entries(output.inputs);
if (!inputs.length) {
throw new Error(`No input files found for ${file}`);
}
const sourceFile = inputs[0][0];
if (!sourceFile) {
throw new Error(`Invalid source file for ${file}`);
}
manifest = await tryReadManifest(sourceFile, manifestExtensions);
if (manifest?.name) {
targetFileName = `${manifest.name}.js`;
}
}
catch (error) {
logger.warn(`Failed to read manifest for ${file}: ${error}`);
}
const targetPath = join(targetDir, targetFileName);
if (addBanner && manifest) {
try {
const banner = generateScriptableBanner(manifest);
const newContent = banner + "\n" + content;
await writeFile(targetPath, newContent);
logger.info(`Deployed with banner: ${file} -> ${targetPath}`);
continue;
}
catch (error) {
logger.warn(`Failed to add banner to ${file}: ${error}`);
}
}
await writeFile(targetPath, content);
logger.info(`Deployed: ${file} -> ${targetPath}`);
}
logger.info(`Successfully deployed ${outputs.length} files`);
}
catch (error) {
logger.error(`Deploy failed: ${error.message}`);
if (!continueOnError) {
throw error;
}
}
});
}
});
}
};
};
}
export { scriptableDeploy };
//# sourceMappingURL=scriptable-deploy.js.map
//# sourceMappingURL=scriptable-deploy.js.map
//# sourceMappingURL=scriptable-deploy.js.map
{
"name": "esbuild-plugin-scriptable",
"description": "An ESBuild plugin for developing Scriptable iOS app scripts with TypeScript, manifest support and auto-deployment",
"version": "0.5.4",
"version": "0.5.5",
"keywords": [

@@ -31,3 +31,3 @@ "scriptable",

"micromatch": "^4.0.8",
"@scriptables/manifest": "0.7.3"
"@scriptables/manifest": "0.7.4"
},

@@ -44,2 +44,3 @@ "devDependencies": {

"tmp": "^0.2.3",
"ts-add-js-extension": "^1.6.5",
"tslib": "^2.8.1",

@@ -70,2 +71,3 @@ "tsup": "^8.3.5",

"build": "tsup",
"postbuild": "ts-add-js-extension --dir=dist",
"clean": "del-cli dist *.tsbuildinfo",

@@ -72,0 +74,0 @@ "test": "pnpm run jest",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc