@stacksjs/actions 0.72.103 → 0.73.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/build/component-libs.js +1 -1
- package/dist/build/libs.d.ts +1 -0
- package/dist/build/libs.js +1 -0
- package/dist/build-function-lib.d.ts +1 -0
- package/dist/build-function-lib.js +1 -0
- package/dist/build-web-component-lib.js +1 -1
- package/dist/deploy/index.js +2 -2
- package/dist/domains/purchase.d.ts +1 -1
- package/dist/generate/lib-entries.js +1 -1
- package/dist/helpers/index.d.ts +0 -2
- package/dist/helpers/utils.js +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1 -1
- package/dist/library/build.d.ts +27 -0
- package/dist/library/build.js +5 -0
- package/dist/library/entries.d.ts +15 -0
- package/dist/library/entries.js +3 -0
- package/dist/library/globals.d.ts +20 -0
- package/dist/library/globals.js +1 -0
- package/dist/library/index.d.ts +11 -0
- package/dist/library/index.js +1 -0
- package/dist/library/manifest.d.ts +34 -0
- package/dist/library/manifest.js +2 -0
- package/dist/library/packages.d.ts +64 -0
- package/dist/library/packages.js +1 -0
- package/dist/library/publish.d.ts +35 -0
- package/dist/library/publish.js +2 -0
- package/dist/library-publish.d.ts +1 -0
- package/dist/library-publish.js +1 -0
- package/dist/make.js +3 -3
- package/dist/setup/ssl.js +1 -1
- package/dist/upgrade/binary.js +1 -1
- package/package.json +24 -23
- package/dist/helpers/lib-entries.d.ts +0 -12
- package/dist/helpers/lib-entries.js +0 -3
- package/dist/helpers/package-json.d.ts +0 -2
- package/dist/helpers/package-json.js +0 -42
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{log}from"@stacksjs/logging";import{buildLibraryPackages}from"../library/build";const reports=await buildLibraryPackages({kinds:["components"]});log.success(`Built ${reports.length} component package${reports.length===1?"":"s"}.`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{log}from"@stacksjs/logging";import{buildLibraryPackages}from"../library/build";const reports=await buildLibraryPackages();log.success(`Built ${reports.length} librar${reports.length===1?"y":"ies"}: ${reports.map((report)=>report.name).join(", ")}`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{log}from"@stacksjs/logging";import{buildLibraryPackages}from"./library/build";const reports=await buildLibraryPackages({kinds:["functions"]});log.success(`Built ${reports.length} function package${reports.length===1?"":"s"}.`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{log}from"@stacksjs/logging";import{buildLibraryPackages}from"./library/build";const reports=await buildLibraryPackages({kinds:["web-components"]});log.success(`Built ${reports.length} web component package${reports.length===1?"":"s"}.`);
|
package/dist/deploy/index.js
CHANGED
|
@@ -146,7 +146,7 @@ Environment=NODE_ENV=production
|
|
|
146
146
|
WantedBy=multi-user.target
|
|
147
147
|
SERVICEFILE
|
|
148
148
|
systemctl daemon-reload
|
|
149
|
-
systemctl enable stacks-api`,"systemctl reset-failed stacks-api || true","systemctl restart stacks-api",`for i in $(seq 1 20); do curl -fsS http://127.0.0.1:${apiServerPort}/api/health && break; sleep 1; done`,`curl -fsS http://127.0.0.1:${apiServerPort}/api/health`,`echo "API server deployed and restarted on port ${apiServerPort}"`].join(" && "),commandId=(await awsClient.request({service:"ssm",region,method:"POST",path:"/",headers:{"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"AmazonSSM.SendCommand"},body:JSON.stringify({InstanceIds:[instanceId],DocumentName:"AWS-RunShellScript",Parameters:{commands:[ssmCommand]},TimeoutSeconds:120})}))?.Command?.CommandId;if(commandId){let attempts=0;while(attempts<30){await new Promise((resolve)=>setTimeout(resolve,3000));try{const invocation=await awsClient.request({service:"ssm",region,method:"POST",path:"/",headers:{"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"AmazonSSM.GetCommandInvocation"},body:JSON.stringify({CommandId:commandId,InstanceId:instanceId})}),status=invocation?.Status;if(status==="Success"){serverDeploySpinner.succeed("API server deployed to EC2");break}else if(status==="Failed"||status==="Cancelled"||status==="TimedOut"){const errOutput=invocation?.StandardErrorContent||"Unknown error";serverDeploySpinner.fail(`API server deploy failed: ${errOutput}`);break}}catch{}attempts++}if(attempts>=30)serverDeploySpinner.fail("API server deploy timed out waiting for SSM command")}else serverDeploySpinner.fail("Failed to send SSM command");try{await s3.deleteObject(
|
|
149
|
+
systemctl enable stacks-api`,"systemctl reset-failed stacks-api || true","systemctl restart stacks-api",`for i in $(seq 1 20); do curl -fsS http://127.0.0.1:${apiServerPort}/api/health && break; sleep 1; done`,`curl -fsS http://127.0.0.1:${apiServerPort}/api/health`,`echo "API server deployed and restarted on port ${apiServerPort}"`].join(" && "),commandId=(await awsClient.request({service:"ssm",region,method:"POST",path:"/",headers:{"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"AmazonSSM.SendCommand"},body:JSON.stringify({InstanceIds:[instanceId],DocumentName:"AWS-RunShellScript",Parameters:{commands:[ssmCommand]},TimeoutSeconds:120})}))?.Command?.CommandId;if(commandId){let attempts=0;while(attempts<30){await new Promise((resolve)=>setTimeout(resolve,3000));try{const invocation=await awsClient.request({service:"ssm",region,method:"POST",path:"/",headers:{"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"AmazonSSM.GetCommandInvocation"},body:JSON.stringify({CommandId:commandId,InstanceId:instanceId})}),status=invocation?.Status;if(status==="Success"){serverDeploySpinner.succeed("API server deployed to EC2");break}else if(status==="Failed"||status==="Cancelled"||status==="TimedOut"){const errOutput=invocation?.StandardErrorContent||"Unknown error";serverDeploySpinner.fail(`API server deploy failed: ${errOutput}`);break}}catch{}attempts++}if(attempts>=30)serverDeploySpinner.fail("API server deploy timed out waiting for SSM command")}else serverDeploySpinner.fail("Failed to send SSM command");try{await s3.deleteObject(bucketName,"_deploy/ts-cloud-dist.js");await s3.deleteObject(bucketName,"_deploy/server.ts")}catch{}}}catch(serverDeployError){serverDeploySpinner.fail(`API server deployment failed: ${serverDeployError.message}`);if(isVerbose)log.debug(`Server deploy error: ${serverDeployError.stack}`)}const emailConfig=config.email;if(emailConfig?.server?.enabled){const mailServerMode=emailConfig?.server?.mode||"server",smtpSpinner=spinner(`Deploying mail server (${mailServerMode} mode) to EC2...`);smtpSpinner.start();try{const{S3Client,AWSClient,EC2Client}=await import("@stacksjs/ts-cloud"),{readFileSync:readSmtpFile,existsSync:smtpFileExists}=await import("node:fs"),{resolve:resolvePath,join:joinPath}=await import("node:path"),{execSync}=await import("node:child_process"),s3=new S3Client(region),awsClient=new AWSClient,ec2=new EC2Client(region),stackName=`${projectName}-cloud`;let smtpInstanceId,smtpBucketName,emailBucketName,securityGroupId;try{const{AWSCloudFormationClient:CfnClient}=await import("@stacksjs/ts-cloud"),cfnOutputs=await new CfnClient(region).getStackOutputs(stackName);for(const[key,value]of Object.entries(cfnOutputs)){if(key.endsWith("InstanceId")&&typeof value==="string")smtpInstanceId=value;if(key==="FrontendBucketName"&&typeof value==="string")smtpBucketName=value;if(key==="emailBucketName"&&typeof value==="string")emailBucketName=value}}catch{}if(!emailBucketName)emailBucketName=`${projectName}-production-s3-email`;if(smtpInstanceId&&smtpBucketName){const mailPorts=[25,80,110,143,465,587,993,995];try{const sgs=(await ec2.describeInstances({Filters:[{Name:"instance-id",Values:[smtpInstanceId]}]}))?.Reservations?.[0]?.Instances?.[0]?.SecurityGroups;if(sgs?.[0]?.GroupId)securityGroupId=sgs[0].GroupId}catch{if(isVerbose)log.debug("Could not get security group for instance")}if(securityGroupId)for(const port of mailPorts)try{await ec2.authorizeSecurityGroupIngress({GroupId:securityGroupId,IpPermissions:[{IpProtocol:"tcp",FromPort:port,ToPort:port,IpRanges:[{CidrIp:"0.0.0.0/0",Description:`Mail port ${port}`}]}]});if(isVerbose)log.debug(` Opened port ${port} on security group ${securityGroupId}`)}catch(e){if(!e.message?.includes("already exists")){if(isVerbose)log.debug(` Port ${port}: ${e.message}`)}}const domain=emailConfig.domain||"stacksjs.com",mailSubdomain=emailConfig?.server?.subdomain||"mail";if(mailServerMode==="server"){if(isVerbose)log.debug("Deploying Zig mail server binary...");try{await installMailBinaryWithPantry()}catch(error){if(isVerbose)log.debug(` Pantry mail install failed: ${error.message}`)}const linuxBinaryPath=await findPantryMailBinary();if(linuxBinaryPath&&smtpFileExists(linuxBinaryPath)){if(!await isElfBinary(linuxBinaryPath)){smtpSpinner.fail(`Pantry installed ${MAIL_PACKAGE_DOMAIN}, but the ${MAIL_TARGET_PLATFORM} binary is not an ELF file.`);throw Error("Linux binary is not a valid ELF file")}const binaryContent=readSmtpFile(linuxBinaryPath);await s3.putObject({bucket:emailBucketName,key:"mail-server/smtp-server",body:binaryContent,contentType:"application/octet-stream"});if(isVerbose)log.debug(` Uploaded Zig binary (${(binaryContent.length/1024/1024).toFixed(1)}MB) to S3`);const zigSsmCommand=["systemctl stop stacks-mail 2>/dev/null || true","systemctl disable stacks-mail 2>/dev/null || true","systemctl stop stacks-smtp 2>/dev/null || true","systemctl disable stacks-smtp 2>/dev/null || true","systemctl stop smtp-server 2>/dev/null || true","mkdir -p /opt/smtp-server /var/lib/smtp-server /var/log/smtp-server /var/spool/mail /etc/smtp-server /var/lib/smtp-server/backups",`aws s3 cp s3://${emailBucketName}/mail-server/smtp-server /opt/smtp-server/smtp-server --region ${region}`,"chmod +x /opt/smtp-server/smtp-server",'file /opt/smtp-server/smtp-server | grep -q ELF || { echo "Binary is not ELF"; exit 1; }',firewallOpenCommand(mailPorts),`cat > /etc/smtp-server/smtp-server.env << 'ENVEOF'
|
|
150
150
|
SMTP_PROFILE=production
|
|
151
151
|
SMTP_HOST=0.0.0.0
|
|
152
152
|
SMTP_PORT=25
|
|
@@ -212,7 +212,7 @@ Environment=AWS_REGION=${region}
|
|
|
212
212
|
${envLines}
|
|
213
213
|
[Install]
|
|
214
214
|
WantedBy=multi-user.target
|
|
215
|
-
SERVICEFILE`,"systemctl daemon-reload","systemctl enable stacks-smtp","systemctl restart stacks-smtp"].join(" && "),cmdId=(await awsClient.request({service:"ssm",region,method:"POST",path:"/",headers:{"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"AmazonSSM.SendCommand"},body:JSON.stringify({InstanceIds:[smtpInstanceId],DocumentName:"AWS-RunShellScript",Parameters:{commands:[smtpSsmCommand]},TimeoutSeconds:180})}))?.Command?.CommandId;if(cmdId){let ssmAttempts=0;while(ssmAttempts<60){await new Promise((resolve)=>setTimeout(resolve,3000));try{const invocation=await awsClient.request({service:"ssm",region,method:"POST",path:"/",headers:{"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"AmazonSSM.GetCommandInvocation"},body:JSON.stringify({CommandId:cmdId,InstanceId:smtpInstanceId})}),status=invocation?.Status;if(status==="Success"){smtpSpinner.succeed("SMTP proxy deployed to EC2");break}else if(status==="Failed"||status==="Cancelled"||status==="TimedOut"){smtpSpinner.fail(`SMTP proxy deploy failed: ${invocation?.StandardErrorContent||"Unknown"}`);break}}catch{}ssmAttempts++}if(ssmAttempts>=60)smtpSpinner.fail("SMTP proxy deploy timed out")}else smtpSpinner.fail("Failed to send SSM command for SMTP proxy");try{await s3.deleteObject(
|
|
215
|
+
SERVICEFILE`,"systemctl daemon-reload","systemctl enable stacks-smtp","systemctl restart stacks-smtp"].join(" && "),cmdId=(await awsClient.request({service:"ssm",region,method:"POST",path:"/",headers:{"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"AmazonSSM.SendCommand"},body:JSON.stringify({InstanceIds:[smtpInstanceId],DocumentName:"AWS-RunShellScript",Parameters:{commands:[smtpSsmCommand]},TimeoutSeconds:180})}))?.Command?.CommandId;if(cmdId){let ssmAttempts=0;while(ssmAttempts<60){await new Promise((resolve)=>setTimeout(resolve,3000));try{const invocation=await awsClient.request({service:"ssm",region,method:"POST",path:"/",headers:{"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"AmazonSSM.GetCommandInvocation"},body:JSON.stringify({CommandId:cmdId,InstanceId:smtpInstanceId})}),status=invocation?.Status;if(status==="Success"){smtpSpinner.succeed("SMTP proxy deployed to EC2");break}else if(status==="Failed"||status==="Cancelled"||status==="TimedOut"){smtpSpinner.fail(`SMTP proxy deploy failed: ${invocation?.StandardErrorContent||"Unknown"}`);break}}catch{}ssmAttempts++}if(ssmAttempts>=60)smtpSpinner.fail("SMTP proxy deploy timed out")}else smtpSpinner.fail("Failed to send SSM command for SMTP proxy");try{await s3.deleteObject(smtpBucketName,"_deploy/smtp-server.js");await s3.deleteObject(smtpBucketName,"_deploy/smtp-entry.ts")}catch{}}else{smtpSpinner.stop();if(isVerbose)log.debug("SMTP server bundle not found, skipping")}}}else{smtpSpinner.stop();if(isVerbose)log.debug("No EC2 instance or bucket found, skipping mail server deploy")}}catch(smtpDeployError){smtpSpinner.fail(`Mail server deployment failed: ${smtpDeployError.message}`);if(isVerbose)log.debug(`Mail server deploy error: ${smtpDeployError.stack}`)}}}const frontendSpinner=spinner("Deploying frontend to S3...");frontendSpinner.start();try{const{S3Client,CloudFormationClient}=await import("@stacksjs/ts-cloud"),{readFileSync}=await import("node:fs"),{buildFrontendStatic}=await import("../build/frontend-static"),s3=new S3Client(region),cf=new CloudFormationClient(region),stackName=`${projectName}-cloud`,stackOutputs=await cf.getStackOutputs(stackName),bucketName=stackOutputs.FrontendBucketName;if(!bucketName){frontendSpinner.stop();console.log("\u26A0 Frontend bucket not found in stack outputs (add storage.public to infrastructure config)")}else{const buildDir=await buildFrontendStatic();await(async(dir,_prefix="")=>{await traverseDirectory(dir,async(filePath,key)=>{const content=readFileSync(filePath),contentType=getMimeType(filePath),cacheControl=key==="index.html"||key.endsWith("/index.html")?"no-cache, no-store, must-revalidate":"public, max-age=31536000, immutable";await withS3Retry(()=>s3.putObject({bucket:bucketName,key,body:content,contentType,cacheControl}),`upload ${key}`);if(isVerbose)log.debug(` Uploaded: ${key}`)})})(buildDir);const bootstrapPath=p.projectPath("bootstrap");if(existsSync(bootstrapPath)){const bootstrapContent=readFileSync(bootstrapPath);await withS3Retry(()=>s3.putObject({bucket:bucketName,key:"install",body:bootstrapContent,contentType:"text/plain",cacheControl:"no-cache, no-store, must-revalidate"}),"upload bootstrap install script");if(isVerbose)log.debug(" Uploaded bootstrap install script")}const distributionId=stackOutputs.publicCloudFrontDistributionId||stackOutputs.CloudFrontDistributionId;if(distributionId){if(isVerbose)log.debug(" Invalidating CloudFront cache...");const{AWSClient}=await import("@stacksjs/ts-cloud");await new AWSClient().request({service:"cloudfront",region:"us-east-1",method:"POST",path:`/2020-05-31/distribution/${distributionId}/invalidation`,headers:{"Content-Type":"application/xml"},body:`<?xml version="1.0" encoding="UTF-8"?>
|
|
216
216
|
<InvalidationBatch xmlns="http://cloudfront.amazonaws.com/doc/2020-05-31/">
|
|
217
217
|
<CallerReference>${Date.now()}</CallerReference>
|
|
218
218
|
<Paths>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{library}from"@stacksjs/config";import{log}from"@stacksjs/logging";import{
|
|
1
|
+
import{library}from"@stacksjs/config";import{log}from"@stacksjs/logging";import{generateLibraryPackages}from"../library/build";export async function generateLibEntries(){if(library?.releaseable===!1){log.info("library.releaseable is false. Skipping library entry points.");return}log.info("Generating library entry points...");const reports=await generateLibraryPackages({allowEmpty:!0});for(const report of reports)log.success(`Generated ${report.name} (${report.kind}, ${report.sources} sources)`)}await generateLibEntries();
|
package/dist/helpers/index.d.ts
CHANGED
package/dist/helpers/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var {require}=import.meta;import{existsSync}from"node:fs";import{delimiter,join}from"node:path";import process from"node:process";import{buddyOptions,runCommand}from"@stacksjs/cli";import{err}from"@stacksjs/error-handling";import{log}from"@stacksjs/logging";import*as p from"@stacksjs/path";export function publishedActionCandidates(action,packageRoot){let root=packageRoot;if(!root)try{const pkgUrl=import.meta.resolve("@stacksjs/actions/package.json");if(pkgUrl){const pkgPath=new URL(pkgUrl).pathname;root=pkgPath.slice(0,pkgPath.lastIndexOf("/"))}}catch{return[]}if(!root)return[];return[`${root}/dist/${action}.js`,`${root}/dist/src/${action}.js`,`${root}/src/${action}.ts`]}export function developmentConditionForProject(projectRoot){return existsSync(join(projectRoot,"storage/framework/core"))&&existsSync(join(projectRoot,"node_modules/@stacksjs/env/src/index.ts"))?"--conditions development":""}export function actionNodePath(projectRoot,existingNodePath){const nodeModulesPath=join(projectRoot,"node_modules"),pantryPath=join(projectRoot,"pantry"),paths=[nodeModulesPath];for(const entry of existingNodePath?.split(delimiter)??[])if(entry&&entry!==nodeModulesPath&&entry!==pantryPath&&!paths.includes(entry))paths.push(entry);paths.push(pantryPath);return paths.join(delimiter)}async function resolveActionFile(action,projectRoot){const candidates=[];if(projectRoot)candidates.push(join(projectRoot,`storage/framework/core/actions/src/${action}.ts`));candidates.push(p.actionsPath(`src/${action}.ts`));candidates.push(...publishedActionCandidates(action));for(const candidate of candidates)if(await Bun.file(candidate).exists())return candidate;return null}export async function runAction(action,options){log.debug(`[action] Running: ${action}`);if(action==="dev/views")try{const nodePath=actionNodePath(p.projectPath(),process.env.NODE_PATH);if(process.env.NODE_PATH!==nodePath){process.env.NODE_PATH=nodePath;require("module").Module._initPaths?.()}const viewsEntries=[p.projectPath("storage/framework/core/actions/src/dev/views.ts"),p.frameworkPath("actions/src/dev/views.ts"),...publishedActionCandidates("dev/views")];for(const entry of viewsEntries)if(existsSync(entry)){await import(entry);return
|
|
1
|
+
var {require}=import.meta;import{existsSync}from"node:fs";import{delimiter,join}from"node:path";import process from"node:process";import{buddyOptions,runCommand}from"@stacksjs/cli";import{err,ok}from"@stacksjs/error-handling";import{log}from"@stacksjs/logging";import*as p from"@stacksjs/path";export function publishedActionCandidates(action,packageRoot){let root=packageRoot;if(!root)try{const pkgUrl=import.meta.resolve("@stacksjs/actions/package.json");if(pkgUrl){const pkgPath=new URL(pkgUrl).pathname;root=pkgPath.slice(0,pkgPath.lastIndexOf("/"))}}catch{return[]}if(!root)return[];return[`${root}/dist/${action}.js`,`${root}/dist/src/${action}.js`,`${root}/src/${action}.ts`]}export function developmentConditionForProject(projectRoot){return existsSync(join(projectRoot,"storage/framework/core"))&&existsSync(join(projectRoot,"node_modules/@stacksjs/env/src/index.ts"))?"--conditions development":""}export function actionNodePath(projectRoot,existingNodePath){const nodeModulesPath=join(projectRoot,"node_modules"),pantryPath=join(projectRoot,"pantry"),paths=[nodeModulesPath];for(const entry of existingNodePath?.split(delimiter)??[])if(entry&&entry!==nodeModulesPath&&entry!==pantryPath&&!paths.includes(entry))paths.push(entry);paths.push(pantryPath);return paths.join(delimiter)}async function resolveActionFile(action,projectRoot){const candidates=[];if(projectRoot)candidates.push(join(projectRoot,`storage/framework/core/actions/src/${action}.ts`));candidates.push(p.actionsPath(`src/${action}.ts`));candidates.push(...publishedActionCandidates(action));for(const candidate of candidates)if(await Bun.file(candidate).exists())return candidate;return null}export async function runAction(action,options){log.debug(`[action] Running: ${action}`);if(action==="dev/views")try{const nodePath=actionNodePath(p.projectPath(),process.env.NODE_PATH);if(process.env.NODE_PATH!==nodePath){process.env.NODE_PATH=nodePath;require("module").Module._initPaths?.()}const viewsEntries=[p.projectPath("storage/framework/core/actions/src/dev/views.ts"),p.frameworkPath("actions/src/dev/views.ts"),...publishedActionCandidates("dev/views")];for(const entry of viewsEntries)if(existsSync(entry)){await import(entry);return ok({})}return err(Error("dev/views entry not found"))}catch(error){return err(Error(`Failed to start dev server: ${error}`))}const isLikelyCoreAction=action.includes("/")||["dev","build","install","upgrade","migrate"].some((prefix)=>action.startsWith(prefix)),userActionsBase=options?.cwd?join(String(options.cwd),"app/Actions"):p.userActionsPath();if(!isLikelyCoreAction&&existsSync(userActionsBase)){const glob=new Bun.Glob("**/*.{ts,js}"),scanOptions={cwd:userActionsBase,onlyFiles:!0,absolute:!0},matchingFiles=[],basePath=userActionsBase;for await(const file of glob.scan(scanOptions)){if(file.replace(`${basePath}/`,"").replace(/\.(ts|js)$/,"")===action||file.endsWith(`${action}.ts`)||file.endsWith(`${action}.js`)){log.debug(`[action] Resolved: ${action} \u2192 ${file}`);return await(await import(file)).default.handle(void 0)}matchingFiles.push(file)}for(const file of matchingFiles)try{const a=await import(file);if(a.name===action){log.debug(`[action] Resolved: ${action} \u2192 ${file}`);return await a.handle()}}catch(error){}}const path=await resolveActionFile(action,options?.cwd?String(options.cwd):void 0);if(!path)return err(Error(`Action '${action}' not found in storage/framework/core/actions/src or @stacksjs/actions`));log.debug(`[action] Resolved: ${action} \u2192 ${path}`);const isDevAction=action.startsWith("dev/"),watchFlag=developmentWatchFlag(action),developmentCondition=developmentConditionForProject(p.projectPath()),opts=isDevAction?"":buddyOptions(options)||"",cmd=["bun",developmentCondition,watchFlag,path,opts].filter(Boolean).join(" "),nodePath=actionNodePath(p.projectPath(),process.env.NODE_PATH),shouldInherit=options?.verbose||isDevAction&&!options?.quiet,optionsWithCwd={cwd:options?.cwd||p.projectPath(),...options,stdout:shouldInherit?"inherit":void 0,stderr:shouldInherit?"inherit":void 0,env:{...options?.env,NODE_PATH:nodePath,...isDevAction?{STACKS_DEV_SERVER:"1"}:{}}},result=await runCommand(cmd,optionsWithCwd);log.debug(`[action] Completed: ${action}`);return result}export function developmentWatchFlag(action){if(!action.startsWith("dev/"))return"";return action==="dev/dashboard"||action==="dev/desktop"?"":"--watch"}export async function runActions(actions,options){if(!actions.length)return err("No actions were specified");for(const action of actions)if(!hasAction(action))return err(`The specified action "${action}" does not exist`);return await runActionSequence(actions,options)}export async function runActionSequence(actions,options,runner=runAction){let result;for(const action of actions){result=await runner(action,options);if(result?.isErr)return result}return result}export function hasAction(action){const userActionPatterns=[`${action}.ts`,`${action}`,`Dashboard/${action}.ts`,`Dashboard/${action}`,`Buddy/${action}.ts`,`Buddy/${action}`],actionPatterns=[`src/${action}.ts`,`src/${action}`,`${action}.ts`,`${action}`];return[...userActionPatterns.map((pattern)=>p.userActionsPath(pattern)),...actionPatterns.map((pattern)=>p.actionsPath(pattern)),...publishedActionCandidates(action)].some((candidate)=>existsSync(candidate))}
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,10 @@ export { commit as runCommit } from './commit';
|
|
|
21
21
|
export * from './dev/index';
|
|
22
22
|
export * from './generate/index';
|
|
23
23
|
export * from './helpers/index';
|
|
24
|
+
// Library packages built out of resources/functions and resources/components.
|
|
25
|
+
// The config-level surface only — `./library/build` and `./library/publish`
|
|
26
|
+
// stay out so importing this barrel never pulls the stx compiler.
|
|
27
|
+
export * from './library/index';
|
|
24
28
|
// Code-style actions, exported so commands import + call them directly
|
|
25
29
|
// (`import { lintProject, lintFix } from '@stacksjs/actions'`) instead of
|
|
26
30
|
// spawning a deep dist path. Exported from ./lint/lint — the pure module — so
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./action";export{add as runAdd}from"./add";export{BlogAdminError,deleteBlogPost,getBlogPost,listBlogPosts,saveBlogPost,slugify as blogSlugify}from"./blog-admin";export{commit as runCommit}from"./commit";export*from"./dev";export*from"./generate";export*from"./helpers";export{formatProject,lintFix,lintProject}from"./lint/lint";export{DEFAULT_STX_LINT_CONFIG,loadStxLintConfig,runStxLint}from"./lint/stx-gate";export{measureDefaultsDrift,summarizeStructureChanges}from"./upgrade/package-project";export*from"./setup";export{createComponent,createDatabase,createFactory,createFunction,createLanguage,createMiddleware,createMigration,createModel,createMail,createNotification,createPage,isDryRunActive,makeAction,makeCertificate,makeComponent,makeDatabase,makeFunction,makeLanguage,makeMail,makeNotification,makePage,makeQueueTable,makeStack,make as runMake,setDryRun}from"./make";export{discoverPackages}from"./discover-packages";export{parseFields,scaffoldCrud}from"./scaffold-crud";export{installStack,uninstallStack,listStacks}from"./stacks";export{makeJob}from"./make-job";export{makeCommand}from"./make-command";export{makePolicy}from"./make-policy";export{makeResource}from"./make-resource";
|
|
1
|
+
export*from"./action";export{add as runAdd}from"./add";export{BlogAdminError,deleteBlogPost,getBlogPost,listBlogPosts,saveBlogPost,slugify as blogSlugify}from"./blog-admin";export{commit as runCommit}from"./commit";export*from"./dev";export*from"./generate";export*from"./helpers";export*from"./library";export{formatProject,lintFix,lintProject}from"./lint/lint";export{DEFAULT_STX_LINT_CONFIG,loadStxLintConfig,runStxLint}from"./lint/stx-gate";export{measureDefaultsDrift,summarizeStructureChanges}from"./upgrade/package-project";export*from"./setup";export{createComponent,createDatabase,createFactory,createFunction,createLanguage,createMiddleware,createMigration,createModel,createMail,createNotification,createPage,isDryRunActive,makeAction,makeCertificate,makeComponent,makeDatabase,makeFunction,makeLanguage,makeMail,makeNotification,makePage,makeQueueTable,makeStack,make as runMake,setDryRun}from"./make";export{discoverPackages}from"./discover-packages";export{parseFields,scaffoldCrud}from"./scaffold-crud";export{installStack,uninstallStack,listStacks}from"./stacks";export{makeJob}from"./make-job";export{makeCommand}from"./make-command";export{makePolicy}from"./make-policy";export{makeResource}from"./make-resource";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { LibraryConfig } from '@stacksjs/types';
|
|
2
|
+
import type { ResolvedLibraryPackage } from './packages';
|
|
3
|
+
/**
|
|
4
|
+
* Stage, generate and build every configured library package of the given
|
|
5
|
+
* kinds. Returns one report per package so callers can say what they built
|
|
6
|
+
* instead of exiting 0 in silence.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildLibraryPackages(options?: LibraryPackageBuildOptions): Promise<LibraryBuildReport[]>;
|
|
9
|
+
/** Stage sources and write manifests without compiling. */
|
|
10
|
+
export declare function generateLibraryPackages(options?: Omit<LibraryPackageBuildOptions, 'compile'>): Promise<LibraryBuildReport[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Library packages follow the project version by default, so a release tags
|
|
13
|
+
* one version and every package it publishes carries it.
|
|
14
|
+
*/
|
|
15
|
+
export declare function projectVersion(): Promise<string>;
|
|
16
|
+
export declare interface LibraryBuildReport {
|
|
17
|
+
name: string
|
|
18
|
+
kind: string
|
|
19
|
+
dir: string
|
|
20
|
+
sources: number
|
|
21
|
+
}
|
|
22
|
+
export declare interface LibraryPackageBuildOptions {
|
|
23
|
+
kinds?: Array<ResolvedLibraryPackage['kind']>
|
|
24
|
+
config?: LibraryConfig
|
|
25
|
+
compile?: boolean
|
|
26
|
+
allowEmpty?: boolean
|
|
27
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{mkdir,rm}from"node:fs/promises";import{dirname,relative,resolve}from"node:path";import{transpilePackage,validateDeclarations,validateRuntimeExports}from"@stacksjs/build";import{library}from"@stacksjs/config";import{log}from"@stacksjs/logging";import{projectPath}from"@stacksjs/path";import{buildComponentLibrary}from"@stacksjs/stx";import{functionEntryData}from"./entries";import{ambientGlobalsUsed,stxAmbientGlobals}from"./globals";import{libraryManifest,serializeManifest}from"./manifest";import{LibraryConfigError,resolveLibraryPackages}from"./packages";export async function buildLibraryPackages(options={}){const config=options.config??library,kinds=options.kinds,compile=options.compile??!0,all=await resolveLibraryPackages(config,{onUnmatched:options.allowEmpty?"skip":"error"}),packages=kinds?all.filter((pkg)=>kinds.includes(pkg.kind)):all;if(!packages.length){if(options.allowEmpty){log.info("No library packages are configured. Nothing to build.");return[]}throw new LibraryConfigError(kinds?`No ${kinds.join("/")} packages are configured. Add one to \`packages\` in config/library.ts.`:"No library packages are configured. Add one to `packages` in config/library.ts.")}const version=await projectVersion(),reports=[];for(const pkg of packages){const verb=compile?"Building":"Generating";log.info(`${verb} ${pkg.name} (${pkg.kind}, ${pkg.sources.length} source${pkg.sources.length===1?"":"s"})...`);if(compile)await rm(resolve(pkg.dir,"dist"),{recursive:!0,force:!0});await mkdir(pkg.dir,{recursive:!0});if(pkg.kind==="functions")await buildFunctionPackage(pkg,config,version,compile);else await buildComponentPackage(pkg,config,version,compile);reports.push({name:pkg.name,kind:pkg.kind,dir:pkg.dir,sources:pkg.sources.length});if(compile)log.success(`Built ${pkg.name} \u2192 ${relative(projectPath(),pkg.dir)}/dist`)}return reports}export async function generateLibraryPackages(options={}){return buildLibraryPackages({...options,compile:!1})}async function buildFunctionPackage(pkg,config,version,compile){await assertPublishable(pkg);const srcDir=resolve(pkg.dir,"src");await rm(srcDir,{recursive:!0,force:!0});const staged=[];for(const source of pkg.sources){const destination=resolve(srcDir,relative(pkg.sourceDir,source));await mkdir(dirname(destination),{recursive:!0});await Bun.write(destination,await Bun.file(source).text());staged.push(destination)}const entry=resolve(srcDir,"index.ts");if(staged.includes(entry))throw new LibraryConfigError(`Package "${pkg.name}" claims resources/functions/index.ts, which collides with the generated barrel. Rename it or exclude it.`);await Bun.write(entry,functionEntryData(pkg,staged));await writeManifest(pkg,config,version);if(!compile)return;await transpilePackage({dir:pkg.dir,pkgName:pkg.name,external:["@stacksjs/*","bun",...Object.keys(pkg.dependencies),...Object.keys(pkg.peerDependencies)]});await validateDeclarations(pkg.dir);await validateRuntimeExports(pkg.dir)}async function buildComponentPackage(pkg,config,version,compile){if(!compile){await writeManifest(pkg,config,version);return}const result=await buildComponentLibrary({inputDir:pkg.sourceDir,outputDir:resolve(pkg.dir,"dist"),prefix:pkg.prefix,components:pkg.sources.map((source)=>{const file=relative(pkg.sourceDir,source),stem=file.replace(/\.stx$/,"").split("/").at(-1);return pkg.names[stem]?{file,name:pkg.names[stem]}:{file}}),progressive:!0,manifest:!0,declarations:!0,css:!0,bundle:!0,minify:!0,sourcemap:pkg.sourcemap?"external":!1});log.debug(`${pkg.name}: compiled ${result.components.length} components (${result.totalBytes} bytes)`);await writeManifest(pkg,config,version)}async function assertPublishable(pkg){if(pkg.runtime==="stx")return;const globals=await stxAmbientGlobals();if(!globals.size)return;const offenders=[];for(const source of pkg.sources){const used=ambientGlobalsUsed(await Bun.file(source).text(),globals);if(used.length)offenders.push(` ${relative(projectPath(),source)} \u2192 ${used.join(", ")}`)}if(!offenders.length)return;throw new LibraryConfigError(`Package "${pkg.name}" uses stx's ambient globals, which no module exports:
|
|
2
|
+
${offenders.join(`
|
|
3
|
+
`)}
|
|
4
|
+
|
|
5
|
+
A consumer importing the published package would hit a ReferenceError. Either import those names explicitly in the source, or set \`runtime: 'stx'\` on the package in config/library.ts to declare that it is only ever consumed from inside an stx app.`)}async function writeManifest(pkg,config,version){const manifest=libraryManifest(pkg,config,version,pkg.sources.map((source)=>relative(projectPath(),source)));await Bun.write(resolve(pkg.dir,"package.json"),serializeManifest(withRuntime(manifest,pkg)))}function withRuntime(manifest,pkg){if(pkg.runtime!=="stx")return manifest;return{...manifest,stacks:{...manifest.stacks,runtime:"stx"}}}export async function projectVersion(){return(await Bun.file(projectPath("package.json")).json()).version??"0.0.0"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ResolvedLibraryPackage } from './packages';
|
|
2
|
+
/**
|
|
3
|
+
* Build the barrel a `functions` package publishes.
|
|
4
|
+
*
|
|
5
|
+
* Specifiers are relative to the entry, which sits beside the copied sources
|
|
6
|
+
* in the package's own `src/`. They used to be absolute paths off the
|
|
7
|
+
* generating machine's home directory — the file is gitignored, so it was
|
|
8
|
+
* regenerated everywhere and the breakage only ever showed up as a build that
|
|
9
|
+
* worked locally and nowhere else.
|
|
10
|
+
*
|
|
11
|
+
* Component packages have no entry here: `buildComponentLibrary` compiles the
|
|
12
|
+
* `.stx` sources and emits its own index, and each generated component
|
|
13
|
+
* registers its custom element on import.
|
|
14
|
+
*/
|
|
15
|
+
export declare function functionEntryData(pkg: ResolvedLibraryPackage, stagedSources: string[]): string;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{relative}from"node:path";export function functionEntryData(pkg,stagedSources){const lines=[`// Generated by \`buddy generate:entries\` for ${pkg.name}. Do not edit.`,""];for(const source of stagedSources){const path=relative(`${pkg.dir}/src`,source),specifier=`./${path}`,alias=pkg.aliases[path.replace(/\.ts$/,"")];lines.push(alias?`export * as ${alias} from '${specifier}'`:`export * from '${specifier}'`)}return`${lines.join(`
|
|
2
|
+
`)}
|
|
3
|
+
`}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The identifiers stx injects into an STX page entry.
|
|
3
|
+
*
|
|
4
|
+
* They are ambient — `stx.d.ts` declares them, the runtime provides them, and
|
|
5
|
+
* NO module exports them. That is fine inside an app and fatal inside a
|
|
6
|
+
* published package: `export const count = state(0)` compiles cleanly and
|
|
7
|
+
* throws `ReferenceError: state is not defined` the first time a consumer
|
|
8
|
+
* imports it. Reading the declarations rather than hardcoding a list means
|
|
9
|
+
* this tracks whatever stx version is installed.
|
|
10
|
+
*/
|
|
11
|
+
export declare function stxAmbientGlobals(): Promise<Set<string>>;
|
|
12
|
+
/**
|
|
13
|
+
* Which ambient globals a source file leans on.
|
|
14
|
+
*
|
|
15
|
+
* Deliberately conservative: a name is only reported when the file neither
|
|
16
|
+
* imports nor declares it, and property accesses (`foo.state`), object keys
|
|
17
|
+
* and string contents are skipped, so the check does not invent work for an
|
|
18
|
+
* author whose file happens to contain the word.
|
|
19
|
+
*/
|
|
20
|
+
export declare function ambientGlobalsUsed(source: string, globals: Set<string>): string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{existsSync}from"node:fs";import{projectPath}from"@stacksjs/path";export async function stxAmbientGlobals(){const file=[projectPath("node_modules/@stacksjs/stx/stx.d.ts"),projectPath("storage/framework/core/stx/stx.d.ts")].find((candidate)=>existsSync(candidate));if(!file)return new Set;const source=await Bun.file(file).text(),names=new Set;for(const match of source.matchAll(/^declare (?:function|const|class)\s+([A-Za-z_$][\w$]*)/gm))names.add(match[1]);return names}export function ambientGlobalsUsed(source,globals){const stripped=source.replace(/\/\*[\s\S]*?\*\//g," ").replace(/\/\/[^\n]*/g," ").replace(/(['"`])(?:\\.|(?!\1)[^\\])*\1/g,'""'),used=new Set;for(const match of stripped.matchAll(/(^|[^.\w$])([A-Za-z_$][\w$]*)\s*(?=[([<.,;)\]}=+\-*/?:]|$)/gm)){const name=match[2];if(globals.has(name))used.add(name)}if(!used.size)return[];for(const name of[...used]){const declared=new RegExp(`(?:^|[^.\\w$])(?:const|let|var|function|class)\\s+${name}\\b`,"m").test(stripped),imported=new RegExp(`import[^;]*\\b${name}\\b[^;]*from`,"m").test(stripped);if(declared||imported)used.delete(name)}return[...used].sort()}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// The config-level surface: resolving `config/library.ts` into packages, and
|
|
2
|
+
// the shapes generated from them.
|
|
3
|
+
//
|
|
4
|
+
// `./build` and `./publish` are deliberately NOT re-exported here. They pull
|
|
5
|
+
// in the stx compiler and the framework build helpers, and this module is
|
|
6
|
+
// reached from the `@stacksjs/actions` barrel, which the CLI loads to render
|
|
7
|
+
// `buddy libs`. Import them by path from the action files that run them.
|
|
8
|
+
export * from './entries';
|
|
9
|
+
export * from './globals';
|
|
10
|
+
export * from './manifest';
|
|
11
|
+
export * from './packages';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"./entries";export*from"./globals";export*from"./manifest";export*from"./packages";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { LibraryConfig } from '@stacksjs/types';
|
|
2
|
+
import type { ResolvedLibraryPackage } from './packages';
|
|
3
|
+
/**
|
|
4
|
+
* Build the package.json a library package publishes.
|
|
5
|
+
*
|
|
6
|
+
* `version` is threaded in rather than read here: every package follows the
|
|
7
|
+
* project version by default, so the release bump has to be the one deciding
|
|
8
|
+
* it, and a package that pinned its own `version` in config keeps that.
|
|
9
|
+
*/
|
|
10
|
+
export declare function libraryManifest(pkg: ResolvedLibraryPackage, config: LibraryConfig | undefined, version: string, sourcesRelativeToProject: string[]): LibraryManifest;
|
|
11
|
+
/** Drop `undefined` fields so the written manifest stays diff-clean. */
|
|
12
|
+
export declare function serializeManifest(manifest: LibraryManifest): string;
|
|
13
|
+
export declare interface LibraryManifest {
|
|
14
|
+
name: string
|
|
15
|
+
type: 'module'
|
|
16
|
+
version: string
|
|
17
|
+
description: string
|
|
18
|
+
author?: string
|
|
19
|
+
contributors?: string[]
|
|
20
|
+
license?: string
|
|
21
|
+
private?: true
|
|
22
|
+
homepage?: string
|
|
23
|
+
repository?: { type: string, url: string, directory: string }
|
|
24
|
+
bugs?: { url: string }
|
|
25
|
+
keywords?: string[]
|
|
26
|
+
exports: Record<string, unknown>
|
|
27
|
+
module: string
|
|
28
|
+
types: string
|
|
29
|
+
files: string[]
|
|
30
|
+
dependencies?: Record<string, string>
|
|
31
|
+
peerDependencies?: Record<string, string>
|
|
32
|
+
publishConfig?: { access: 'public' | 'restricted' }
|
|
33
|
+
stacks: { kind: string, sources: string[], runtime?: 'standalone' | 'stx' }
|
|
34
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export function libraryManifest(pkg,config,version,sourcesRelativeToProject){const cfg=config??{},repository=cfg.repository,directory=`storage/framework/libs/packages/${pkg.slug}`,manifest={name:pkg.name,type:"module",version:pkg.version??version,description:pkg.description,author:cfg.author,contributors:cfg.contributors,license:pkg.license??cfg.license,homepage:repository?`https://github.com/${repository}/tree/main/${directory}#readme`:void 0,repository:repository?{type:"git",url:`git+https://github.com/${repository}.git`,directory}:void 0,bugs:repository?{url:`https://github.com/${repository}/issues`}:void 0,keywords:pkg.keywords.length?pkg.keywords:void 0,exports:{".":{types:"./dist/index.d.js",import:pkg.kind==="web-components"?"./dist/bundle.js":"./dist/index.js",default:pkg.kind==="web-components"?"./dist/bundle.js":"./dist/index.js"},"./*":{types:"./dist/*.d.js",import:"./dist/*.js",default:"./dist/*.js"}},module:pkg.kind==="web-components"?"dist/bundle.js":"dist/index.js",types:"dist/index.d.ts",files:["README.md","dist"],dependencies:Object.keys(pkg.dependencies).length?pkg.dependencies:void 0,peerDependencies:Object.keys(pkg.peerDependencies).length?pkg.peerDependencies:void 0,stacks:{kind:pkg.kind,sources:sourcesRelativeToProject}};if(pkg.private)manifest.private=!0;else manifest.publishConfig={access:pkg.access};if(pkg.kind!=="functions"){manifest.exports["./style.css"]="./dist/bundle.css";manifest.exports["./custom-elements.json"]="./dist/custom-elements.json"}return manifest}export function serializeManifest(manifest){return`${JSON.stringify(manifest,(_key,value)=>value===void 0?void 0:value,2)}
|
|
2
|
+
`}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { LibraryConfig, LibraryPackageKind } from '@stacksjs/types';
|
|
2
|
+
/** Where a package of this kind reads its sources from. */
|
|
3
|
+
export declare function libraryPackageSourceDir(kind: LibraryPackageKind): string;
|
|
4
|
+
/** `@acme/ui` -> `ui`. Used for the default slug, prefix and tag namespace. */
|
|
5
|
+
export declare function unscopedName(name: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Read `config/library.ts` into a flat list of package definitions.
|
|
8
|
+
*
|
|
9
|
+
* `packages` is authoritative when present. The older single-package
|
|
10
|
+
* `functions` / `webComponents` keys are normalized into the same shape, so
|
|
11
|
+
* every consumer downstream only ever deals with a list — and a config written
|
|
12
|
+
* before this existed keeps building exactly one package, as it always did.
|
|
13
|
+
*/
|
|
14
|
+
export declare function normalizeLibraryPackages(config: LibraryConfig | undefined): LibraryPackageDefinition[];
|
|
15
|
+
/**
|
|
16
|
+
* Match each definition against the files actually present in `resources/`.
|
|
17
|
+
*
|
|
18
|
+
* A package that matches nothing is an error, not an empty package: shipping
|
|
19
|
+
* an empty tarball to npm is worse than failing the build, and a typo'd glob
|
|
20
|
+
* is by far the likeliest cause.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveLibraryPackages(config: LibraryConfig | undefined, options?: { onUnmatched?: 'error' | 'skip' }): Promise<ResolvedLibraryPackage[]>;
|
|
23
|
+
/** The import specifier an entry point uses to reach one of its sources. */
|
|
24
|
+
export declare function entrySpecifier(entry: string, source: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* A package definition after normalization, before its sources are matched.
|
|
27
|
+
* Everything here is derivable from config alone, which is what makes the
|
|
28
|
+
* normalization step pure and testable without a filesystem.
|
|
29
|
+
*/
|
|
30
|
+
export declare interface LibraryPackageDefinition {
|
|
31
|
+
name: string
|
|
32
|
+
kind: LibraryPackageKind
|
|
33
|
+
slug: string
|
|
34
|
+
description: string
|
|
35
|
+
keywords: string[]
|
|
36
|
+
license?: string
|
|
37
|
+
version?: string
|
|
38
|
+
private: boolean
|
|
39
|
+
access: 'public' | 'restricted'
|
|
40
|
+
runtime: 'standalone' | 'stx'
|
|
41
|
+
sourcemap: boolean
|
|
42
|
+
include: string[]
|
|
43
|
+
exclude: string[]
|
|
44
|
+
prefix: string
|
|
45
|
+
dependencies: Record<string, string>
|
|
46
|
+
peerDependencies: Record<string, string>
|
|
47
|
+
aliases: Record<string, string>
|
|
48
|
+
names: Record<string, string>
|
|
49
|
+
}
|
|
50
|
+
/** A definition plus the sources it actually claims on disk. */
|
|
51
|
+
export declare interface ResolvedLibraryPackage extends LibraryPackageDefinition {
|
|
52
|
+
sourceDir: string
|
|
53
|
+
sources: string[]
|
|
54
|
+
dir: string
|
|
55
|
+
entry: string
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Raised for a `config/library.ts` that cannot produce a coherent set of
|
|
59
|
+
* packages. Thrown rather than logged-and-exited so the build, generate and
|
|
60
|
+
* publish paths can each decide how loud to be — and so it is testable.
|
|
61
|
+
*/
|
|
62
|
+
export declare class LibraryConfigError extends Error {
|
|
63
|
+
name: string;
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{relative,resolve}from"node:path";import process from"node:process";import{componentsPath,functionsPath,libraryPackagePath}from"@stacksjs/path";export class LibraryConfigError extends Error{name="LibraryConfigError"}const DEFAULT_FUNCTION_INCLUDE=["**/*.ts"],DEFAULT_FUNCTION_EXCLUDE=["**/*.test.ts","**/*.spec.ts","**/*.d.ts","**/_*.ts"],DEFAULT_COMPONENT_INCLUDE=["**/*.stx"],DEFAULT_COMPONENT_EXCLUDE=[];export function libraryPackageSourceDir(kind){return kind==="functions"?functionsPath():componentsPath()}export function unscopedName(name){return name.split("/").at(-1)??name}function includeFromFilesEntry(entry){const[file,alias]=Array.isArray(entry)?entry:[entry,void 0],stem=file.replace(/\.ts$/,""),include=stem==="*"?"*.ts":`${stem}.ts`;return alias?{include,alias:[stem,alias]}:{include}}function toDefinition(option,fallback){const name=option.name?.trim();if(!name)throw new LibraryConfigError(`library.packages[${fallback.index}] has no name. Every package needs the name it is published under.`);if(name.startsWith(".")||name.includes("\\")||name.split("/").length>2)throw new LibraryConfigError(`library.packages[${fallback.index}] has an invalid npm name: "${name}".`);const kind=option.kind??"functions";if(kind!=="functions"&&kind!=="components"&&kind!=="web-components")throw new LibraryConfigError(`Package "${name}" has an unknown kind "${kind}". Expected 'functions', 'components' or 'web-components'.`);const slug=(option.dir??unscopedName(name)).replace(/^@/,"");if(!/^[\w.-]+$/.test(slug))throw new LibraryConfigError(`Package "${name}" resolves to the directory "${slug}", which is not a single safe path segment. Set \`dir\` explicitly.`);const aliases={},includeFromFiles=[];for(const entry of option.files??[]){const{include,alias}=includeFromFilesEntry(entry);includeFromFiles.push(include);if(alias)aliases[alias[0]]=alias[1]}const names={};for(const tag of option.tags??[])if(Array.isArray(tag.name))names[tag.name[0]]=tag.name[1];const defaultInclude=kind==="functions"?DEFAULT_FUNCTION_INCLUDE:DEFAULT_COMPONENT_INCLUDE,defaultExclude=kind==="functions"?DEFAULT_FUNCTION_EXCLUDE:DEFAULT_COMPONENT_EXCLUDE,includeFromTags=option.tags?.length&&!option.include?.length?option.tags.map((tag)=>`${Array.isArray(tag.name)?tag.name[0]:tag.name}.stx`):[],include=option.include?.length?option.include:includeFromFiles.length?includeFromFiles:includeFromTags.length?includeFromTags:defaultInclude;return{name,kind,slug,description:option.description??`The ${name} library.`,keywords:option.keywords??[],license:option.license??fallback.license,version:option.version,private:option.private??!1,access:option.access??"public",runtime:option.runtime??"standalone",sourcemap:option.shouldGenerateSourcemap??!1,include,exclude:option.exclude??defaultExclude,prefix:option.prefix??unscopedName(name).replace(/[^a-z0-9]+/gi,"-").replace(/^-+|-+$/g,"").toLowerCase(),dependencies:option.dependencies??{},peerDependencies:option.peerDependencies??{},aliases,names}}export function normalizeLibraryPackages(config){const cfg=config??{},options=[];if(cfg.packages?.length)options.push(...cfg.packages);else{if(cfg.functions?.name)options.push({...cfg.functions,kind:"functions"});if(cfg.webComponents?.name)options.push({...cfg.webComponents,kind:"web-components"})}const definitions=options.map((option,index)=>toDefinition(option,{license:cfg.license,index})),byName=new Map,bySlug=new Map;for(const definition of definitions){if(byName.has(definition.name))throw new LibraryConfigError(`Two packages are both named "${definition.name}". npm names must be unique.`);byName.set(definition.name,1);const owner=bySlug.get(definition.slug);if(owner)throw new LibraryConfigError(`"${owner}" and "${definition.name}" both build in storage/framework/libs/packages/${definition.slug}. Give one of them a \`dir\`.`);bySlug.set(definition.slug,definition.name)}return definitions}export async function resolveLibraryPackages(config,options={}){const definitions=normalizeLibraryPackages(config),resolved=[];for(const definition of definitions){const sourceDir=libraryPackageSourceDir(definition.kind),sources=await matchSources(sourceDir,definition.include,definition.exclude);if(!sources.length){if(options.onUnmatched==="skip")continue;throw new LibraryConfigError(`Package "${definition.name}" matched no files in ${relative(process.cwd(),sourceDir)} (include: ${definition.include.join(", ")}${definition.exclude.length?`; exclude: ${definition.exclude.join(", ")}`:""}).`)}resolved.push({...definition,sourceDir,sources,dir:libraryPackagePath(definition.slug),entry:libraryPackagePath(definition.slug,"src/index.ts")})}return resolved}async function matchSources(sourceDir,include,exclude){const expand=async(patterns)=>{const files=new Set;for(const pattern of patterns)for await(const file of new Bun.Glob(pattern).scan({cwd:sourceDir,absolute:!0,onlyFiles:!0}))files.add(resolve(file));return files},included=await expand(include),excluded=exclude.length?await expand(exclude):new Set;return[...included].filter((file)=>!excluded.has(file)).sort()}export function entrySpecifier(entry,source){const path=relative(resolve(entry,".."),source).replace(/\.ts$/,"");return path.startsWith(".")?path:`./${path}`}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { LibraryConfig } from '@stacksjs/types';
|
|
2
|
+
/**
|
|
3
|
+
* Work out what a publish would push, and refuse anything that would ship an
|
|
4
|
+
* empty or stale tarball.
|
|
5
|
+
*
|
|
6
|
+
* The framework has shipped a dist-less package to npm before, more than once,
|
|
7
|
+
* because the publish step ran with `--ignore-scripts` and nothing checked
|
|
8
|
+
* that a build had happened first. This is that check, ahead of time.
|
|
9
|
+
*/
|
|
10
|
+
export declare function planLibraryPublish(config?: LibraryConfig | undefined): Promise<LibraryPublishPlan[]>;
|
|
11
|
+
/**
|
|
12
|
+
* The publisher to shell out to.
|
|
13
|
+
*
|
|
14
|
+
* `bun publish` rather than `npm`: this framework runs on Bun and npm is not
|
|
15
|
+
* necessarily installed at all — it is not on the machine this was written on,
|
|
16
|
+
* and hardcoding it turned `libs:publish` into an ENOENT. `npm` is still
|
|
17
|
+
* accepted as a fallback for an environment that has it and not the other.
|
|
18
|
+
*/
|
|
19
|
+
export declare function publishCommand(): string[];
|
|
20
|
+
/**
|
|
21
|
+
* Publish each built package.
|
|
22
|
+
*
|
|
23
|
+
* One package per invocation rather than a workspace-wide publish: these live
|
|
24
|
+
* outside the framework workspace on purpose, and publishing them one at a
|
|
25
|
+
* time means a failure names the package that failed instead of leaving the
|
|
26
|
+
* set half-published with no record of where it stopped.
|
|
27
|
+
*/
|
|
28
|
+
export declare function publishLibraryPackages(options?: { dryRun?: boolean, config?: LibraryConfig }): Promise<LibraryPublishPlan[]>;
|
|
29
|
+
export declare interface LibraryPublishPlan {
|
|
30
|
+
name: string
|
|
31
|
+
version: string
|
|
32
|
+
dir: string
|
|
33
|
+
access: 'public' | 'restricted'
|
|
34
|
+
tarballFiles: number
|
|
35
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{existsSync}from"node:fs";import{relative,resolve}from"node:path";import{log}from"@stacksjs/logging";import{projectPath}from"@stacksjs/path";import{library}from"@stacksjs/config";import{LibraryConfigError,resolveLibraryPackages}from"./packages";export async function planLibraryPublish(config=library){const publishable=(await resolveLibraryPackages(config)).filter((pkg)=>!pkg.private);if(!publishable.length)throw new LibraryConfigError("Every configured library package is marked private. Nothing to publish.");const plans=[],missing=[];for(const pkg of publishable){const manifestPath=resolve(pkg.dir,"package.json"),distDir=resolve(pkg.dir,"dist");if(!existsSync(manifestPath)||!existsSync(distDir)){missing.push(pkg.name);continue}const built=await Array.fromAsync(new Bun.Glob("**/*.{js,css,json,d.ts}").scan({cwd:distDir}));if(!built.length){missing.push(pkg.name);continue}const manifest=await Bun.file(manifestPath).json();plans.push({name:pkg.name,version:manifest.version??"0.0.0",dir:pkg.dir,access:pkg.access,tarballFiles:built.length})}if(missing.length)throw new LibraryConfigError(`These packages have no build output: ${missing.join(", ")}.
|
|
2
|
+
Run \`buddy build:libs\` before publishing.`);return plans}export function publishCommand(){if(Bun.which("bun"))return["bun","publish"];if(Bun.which("npm"))return["npm","publish"];throw Error("Neither `bun` nor `npm` is on PATH, so there is nothing to publish with.")}export async function publishLibraryPackages(options={}){const plans=await planLibraryPublish(options.config??library),command=publishCommand();for(const plan of plans){const args=[...command,"--access",plan.access];if(options.dryRun)args.push("--dry-run");log.info(`${options.dryRun?"[dry run] ":""}${args.join(" ")} (${plan.name}@${plan.version}, ${relative(projectPath(),plan.dir)})`);const proc=Bun.spawnSync({cmd:args,cwd:plan.dir,stdout:"inherit",stderr:"inherit"});if(proc.exitCode!==0)throw Error(`Publishing ${plan.name}@${plan.version} failed with exit code ${proc.exitCode}.`)}return plans}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{parseOptions}from"@stacksjs/cli";import{log}from"@stacksjs/logging";import{publishLibraryPackages}from"./library/publish";const options=parseOptions(),dryRun=options?.dryRun===!0||String(options?.dryRun)==="true",plans=await publishLibraryPackages({dryRun});log.success(dryRun?`Dry run complete. ${plans.length} package(s) would publish: ${plans.map((plan)=>`${plan.name}@${plan.version}`).join(", ")}`:`Published ${plans.map((plan)=>`${plan.name}@${plan.version}`).join(", ")}`);
|
package/dist/make.js
CHANGED
|
@@ -3,7 +3,7 @@ import process from"node:process";import{existsSync}from"node:fs";import{italic,
|
|
|
3
3
|
`),truncated=lines.length>60?`
|
|
4
4
|
${italic(`\u2026 ${lines.length-60} more lines elided`)}`:"";log.info(`[dry-run] would write ${path} (${lines.length} lines)`);process.stdout.write(`${preview}${truncated}
|
|
5
5
|
|
|
6
|
-
`);return}await writeTextFile({path,data})}async function createFileWithTemplate(path,templateKey,...args){await writeOrPreview(path,generateCode(templateKey,...args))}export async function invoke(options){if(options.component)await makeComponent(options);if(options.database)makeDatabase(options);if(options.function)await makeFunction(options);if(options.language)await makeLanguage(options);if(options.middleware)await createMiddleware(options);if(options.notification)await makeNotification(options);if(options.page)await makePage(options);if(options.stack)await makeStack(options)}export async function make(options){return await invoke(options)}export async function makeAction(options){try{const name=options
|
|
6
|
+
`);return}await writeTextFile({path,data})}async function createFileWithTemplate(path,templateKey,...args){await writeOrPreview(path,generateCode(templateKey,...args))}export async function invoke(options){if(options.component)await makeComponent(options);if(options.database)makeDatabase(options);if(options.function)await makeFunction(options);if(options.language)await makeLanguage(options);if(options.middleware)await createMiddleware(options);if(options.notification)await makeNotification(options);if(options.page)await makePage(options);if(options.stack)await makeStack(options)}export async function make(options){return await invoke(options)}function requireName(options,what){const name=options.name?.trim();if(!name){log.error(`A name is required to create a ${what}. Pass one with --name.`);process.exit(ExitCode.InvalidArgument)}return name}export async function makeAction(options){try{const name=requireName(options,"action");log.info("Creating your action...");await createAction(options);log.success(`Created ${italic(name)} action`)}catch(error){log.error("There was an error creating your action",error);process.exit(ExitCode.FatalError)}}export async function makeComponent(options){try{const name=requireName(options,"component");log.info("Creating your component...");await createComponent(options);log.success(`Created ${italic(name)} component`)}catch(error){log.error("There was an error creating your component",error);process.exit(ExitCode.FatalError)}}export async function createAction(options){const name=requireName(options,"component"),opts=options,wantsValidation=Boolean(opts.withValidation||options["with-validation"]),wantsAuth=Boolean(opts.withAuth||options["with-auth"]),templateKey=wantsValidation&&wantsAuth?"actionWithBoth":wantsValidation?"actionWithValidation":wantsAuth?"actionWithAuth":"action";await createFileWithTemplate(p.userActionsPath(name),templateKey,name)}export async function createComponent(options){const name=requireName(options,"component");await createFileWithTemplate(p.userComponentsPath(componentFileName(name)),"component",name)}export function componentFileName(name){return`${name}.stx`}export function makeDatabase(options){try{createDatabase(options)}catch(error){log.error("There was an error creating your database",error);process.exit(ExitCode.FatalError)}}export function createDatabase(options){const name=pascalCase(options.name||"MyModel");console.log(`
|
|
7
7
|
Stacks manages the database through models and migrations, so there is no separate database to create.
|
|
8
8
|
|
|
9
9
|
To add a "${name}" table:
|
|
@@ -12,7 +12,7 @@ import process from"node:process";import{existsSync}from"node:fs";import{italic,
|
|
|
12
12
|
3. buddy migrate
|
|
13
13
|
|
|
14
14
|
To use a different database engine or file, edit the connections in \`config/database.ts\`.
|
|
15
|
-
`)}export async function factory(options){try{const name=options
|
|
15
|
+
`)}export async function factory(options){try{const name=requireName(options,"component");log.info(`Creating your ${italic(name)} factory...`);await createFactory(options)}catch(error){log.error("There was an error creating your factory",error);process.exit(ExitCode.FatalError)}}export async function createFactory(options){const name=options.name||"MyFactory",factoryName=name.endsWith("Factory")?name:`${name}Factory`;try{const{path:p}=await import("@stacksjs/path"),factoryPath=`${p.userDatabasePath("factories")}/${factoryName}.ts`;if(await Bun.file(factoryPath).exists()){log.warn(`Factory ${factoryName} already exists at ${factoryPath}`);return}const modelName=name.replace(/Factory$/,""),content=`import type { ${modelName}Model } from '@stacksjs/orm'
|
|
16
16
|
|
|
17
17
|
export function ${factoryName}(): Partial<${modelName}Model> {
|
|
18
18
|
return {
|
|
@@ -21,4 +21,4 @@ export function ${factoryName}(): Partial<${modelName}Model> {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export default ${factoryName}
|
|
24
|
-
`;await writeOrPreview(factoryPath,content);log.success(`Created factory: ${factoryPath}`)}catch(error){log.error(`Failed to create factory ${factoryName}`,error);throw error}}export async function makeNotification(options){try{const name=options
|
|
24
|
+
`;await writeOrPreview(factoryPath,content);log.success(`Created factory: ${factoryPath}`)}catch(error){log.error(`Failed to create factory ${factoryName}`,error);throw error}}export async function makeNotification(options){try{const name=requireName(options,"notification");log.info(`Creating your ${italic(name)} notification...`);await createNotification(options);log.success(`Created ${italic(name)} notification`)}catch(error){log.error("There was an error creating your notification",error);process.exit(ExitCode.FatalError)}}export async function createMail(options){const rawName=String(options.name||"").trim();if(!rawName){log.error("A mail name is required (e.g. `buddy make:mail OrderShipped`).");return!1}const className=pascalCase(rawName),kebabName=kebabCase(rawName),mailPath=p.userMailPath(`${className}.ts`),templatePath=p.userEmailsPath(`${kebabName}.stx`);if(!options.force){if(existsSync(mailPath)){console.error(`${italic(mailPath)} already exists - re-run with --force to overwrite.`);return!1}if(existsSync(templatePath)){console.error(`${italic(templatePath)} already exists - re-run with --force to overwrite.`);return!1}}if(!doesFolderExist(p.userMailPath()))await createFolder(p.userMailPath());if(!doesFolderExist(p.userEmailsPath()))await createFolder(p.userEmailsPath());try{await createFileWithTemplate(mailPath,"mail",className,kebabName);await createFileWithTemplate(templatePath,"mailTemplate",className,kebabName);return!0}catch(error){handleError("Error creating mailable",error);return!1}}export async function makeMail(options){try{const name=requireName(options,"mail");log.info(`Creating your ${italic(name)} mailable...`);if(!await createMail(options))process.exit(ExitCode.FatalError);log.success(`Created ${italic(pascalCase(name))} mailable + ${italic(kebabCase(name))}.stx template`)}catch(error){log.error("There was an error creating your mailable",error);process.exit(ExitCode.FatalError)}}export async function makePage(options){try{const name=requireName(options,"page");log.info("Creating your page...");await createPage(options);log.success(`Created ${name} page`)}catch(error){log.error("There was an error creating your page",error);process.exit(ExitCode.FatalError)}}export async function createPage(options){const name=requireName(options,"page");await createFileWithTemplate(p.userViewsPath(pageFileName(name)),"page",name)}export function pageFileName(name){return`${name}.stx`}export async function makeFunction(options){try{const name=requireName(options,"function");log.info("Creating your function...");await createFunction(options);log.success(`Created ${name} function`)}catch(error){log.error("There was an error creating your function",error);process.exit(ExitCode.FatalError)}}export async function createFunction(options){const name=requireName(options,"function");await createFileWithTemplate(p.userFunctionsPath(`${name}.ts`),"function",camelCase(name))}export async function makeLanguage(options){try{const name=requireName(options,"language");log.info("Creating your translation file...");await createLanguage(options);log.success(`Created ${name} translation file`)}catch(error){log.error("There was an error creating your language.",error);process.exit(ExitCode.FatalError)}}export async function createLanguage(options){const name=requireName(options,"language");await createFileWithTemplate(p.resourcesPath(`lang/${name}.yml`),"language",name)}export async function makeStack(options){try{const name=requireName(options,"stack");log.info(`Creating your ${name} stack...`);const stackDir=resolve(process.cwd(),name);if(doesFolderExist(stackDir)){log.error(`Directory "${name}" already exists`);process.exit(ExitCode.FatalError)}await createFolder(stackDir);const dirs=["app/Actions","app/Models","config","database/migrations","resources/views","resources/components","resources/functions","resources/partials","routes","public"];for(const dir of dirs)await createFolder(resolve(stackDir,dir));const shortName=name.replace(/^@[^/]+\//,"").replace(/-stack$/,"");await createFileWithTemplate(resolve(stackDir,"package.json"),"stackPackageJson",name,shortName);log.success(`Successfully scaffolded your "${name}" stack`);log.info("");log.info(` cd ${name}`);log.info(" # Add your models, actions, views, etc.");log.info(" # Push the repository, then submit its GitHub source to the Stacks registry.");log.info("");log.info(" Users install it with:");log.info(` buddy add ${shortName}`)}catch(error){log.error("There was an error creating your stack",error);process.exit(ExitCode.FatalError)}}export async function createNotification(options){const name=requireName(options,"stack");try{let importOption="EmailOptions";if(!doesFolderExist("notifications"))await createFolder("./notifications");if(options.chat)importOption="ChatOptions";if(options.sms)importOption="SMSOptions";await createFileWithTemplate(p.userNotificationsPath(`${name}.ts`),"notification",importOption);return!0}catch(error){handleError("Error creating notification",error);return!1}}export async function createMigration(options){const optionName=options.name;if(!optionName)throw Error("options.name is required and cannot be empty");const fileName=`${Date.now()}-${kebabCase(optionName)}.sql`,path=p.userMigrationsPath(fileName),snake=kebabCase(optionName).replace(/-/g,"_"),table=/^create_(.+)_table$/.exec(snake)?.[1]??"table_name",driver=(process.env.DB_CONNECTION||"sqlite").toLowerCase(),mysqlLike=driver==="mysql"||driver==="singlestore",tableIdentifier=mysqlLike?`\`${table}\``:`"${table}"`,primaryKey=driver==="postgres"?'"id" BIGSERIAL PRIMARY KEY':mysqlLike?"`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY":'"id" INTEGER PRIMARY KEY AUTOINCREMENT';try{await createFileWithTemplate(path,"migration",tableIdentifier,primaryKey);log.success(`Migration created: ${italic(`database/migrations/${fileName}`)}`)}catch(error){log.error(error)}}export async function makeQueueTable(){await runAction(Action.QueueTable)}export async function makeCertificate(){const domain=await localUrl();log.info("Creating SSL certificate...");await runCommand(`tlsx ${domain}`,{cwd:p.storagePath("keys")});log.success("Certificate created");log.info("Installing SSL certificate...");await runCommand("tlsx -install",{cwd:p.storagePath("keys")});log.success("Certificate installed")}export async function createModel(options){const optionName=options.name;if(!optionName)throw Error("options.name is required and cannot be empty");const name=optionName.charAt(0).toUpperCase()+optionName.slice(1),tableName=name.replace(/([A-Z])/g,"_$1").toLowerCase().replace(/^_/,"").replace(/([^s])$/,"$1s"),path=p.userModelsPath(`${name}.ts`);try{await createFileWithTemplate(path,"model",name,tableName);log.success(`Model created: ${italic(`app/Models/${name}.ts`)}`)}catch(error){log.error(error)}}export async function createMiddleware(options){const name=requireName(options,"certificate");await createFileWithTemplate(p.userMiddlewarePath(`${name}.ts`),"middleware",name)}
|
package/dist/setup/ssl.js
CHANGED
|
@@ -2,4 +2,4 @@ import{execSync,spawn}from"node:child_process";import fs from"node:fs";import os
|
|
|
2
2
|
# Added by Stacks
|
|
3
3
|
127.0.0.1 ${domain}
|
|
4
4
|
::1 ${domain}
|
|
5
|
-
`;if(process.platform==="win32"){log.warn("Please add this to your hosts file manually:");log.warn(`127.0.0.1 ${domain}`);log.warn(`::1 ${domain}`);log.warn(`File location: ${HOSTS_FILE}`);return!1}if(!canRunSudo()){if(verbose){log.warn(`Cannot add ${domain} to hosts file (non-interactive mode and no SUDO_PASSWORD set)`);log.info("Run `buddy setup:ssl` in a terminal or set SUDO_PASSWORD environment variable")}return!1}const sudoPassword=getSudoPassword();return new Promise((resolve)=>{let child;if(sudoPassword)child=spawn("sh",["-c",`echo '${sudoPassword}' | sudo -S sh -c "echo '${hostsEntry}' >> '${HOSTS_FILE}'"`],{stdio:["pipe","inherit","inherit"]});else child=spawn("sudo",["sh","-c",`echo '${hostsEntry}' >> "${HOSTS_FILE}"`],{stdio:"inherit"});child.on("close",(code)=>{if(code===0){if(verbose)log.success(`Added ${domain} to hosts file`);resolve(!0)}else{if(verbose){log.error(`Failed to add ${domain} to hosts file`);log.warn(`Manually add: 127.0.0.1 ${domain}`)}resolve(!1)}});child.on("error",()=>{log.error("Failed to run sudo command");resolve(!1)})})}export function sslCertificatesExist(domain){const certPath=path.join(SSL_DIR,`${domain}.crt`),keyPath=path.join(SSL_DIR,`${domain}.key`),caPath=path.join(SSL_DIR,`${domain}.ca.crt`);return fs.existsSync(certPath)&&fs.existsSync(keyPath)&&fs.existsSync(caPath)}export function isCertificateTrusted(domain){if(process.platform!=="darwin")return!0;const certPath=path.join(SSL_DIR,`${domain}.crt`);if(!fs.existsSync(certPath))return!1;try{const fingerprint=execSync(`openssl x509 -noout -fingerprint -sha256 -in "${certPath}"`,{encoding:"utf-8"}),eqIdx=fingerprint.indexOf("="),fingerprintValue=eqIdx>=0?fingerprint.slice(eqIdx+1).trim():"";if(!fingerprintValue)return!1;return execSync("security find-certificate -a -Z -p | openssl x509 -noout -fingerprint -sha256 2>/dev/null || true",{encoding:"utf-8"}).includes(fingerprintValue)}catch{return!1}}export async function trustCertificate(domain,verbose){const caCertPath=path.join(SSL_DIR,`${domain}.ca.crt`),certPath=path.join(SSL_DIR,`${domain}.crt`);if(!fs.existsSync(caCertPath)){log.error(`CA certificate not found: ${caCertPath}`);return!1}if(isCertificateTrusted(domain)){if(verbose)log.info("Certificate is already trusted, skipping trust step");return!0}if(!canRunSudo()){if(verbose){log.warn("Cannot trust certificate (non-interactive mode and no SUDO_PASSWORD set)");log.info("Run `buddy setup:ssl` in a terminal or set SUDO_PASSWORD environment variable")}return!1}const sudoPassword=getSudoPassword();if(process.platform==="darwin")return new Promise((resolve)=>{const combinedCommand=`security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain '${caCertPath}' && security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain '${certPath}'`;let child;if(sudoPassword)child=spawn("sh",["-c",`echo '${sudoPassword}' | sudo -S sh -c "${combinedCommand}"`],{stdio:["pipe","inherit","inherit"]});else child=spawn("sudo",["sh","-c",combinedCommand],{stdio:"inherit"});child.on("close",(code)=>{if(code===0){try{execSync(`security add-trusted-cert -d -r trustRoot -k ~/Library/Keychains/login.keychain-db "${certPath}" 2>/dev/null || true`)}catch{}resolve(!0)}else{if(verbose){log.warn("Could not trust certificate automatically");log.info(` sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${caCertPath}"`)}resolve(!1)}});child.on("error",()=>{log.error("Failed to run sudo command");resolve(!1)})});else if(process.platform==="linux")return new Promise((resolve)=>{const linuxCommands=`mkdir -p /usr/local/share/ca-certificates/stacks && cp '${caCertPath}' /usr/local/share/ca-certificates/stacks/ && update-ca-certificates`;let child;if(sudoPassword)child=spawn("sh",["-c",`echo '${sudoPassword}' | sudo -S sh -c "${linuxCommands}"`],{stdio:["pipe","inherit","inherit"]});else child=spawn("sudo",["sh","-c",linuxCommands],{stdio:"inherit"});child.on("close",(code)=>{if(code===0){log.success("Certificate trusted");resolve(!0)}else{log.warn("Could not trust certificate automatically");resolve(!1)}});child.on("error",()=>resolve(!1))});else if(process.platform==="win32"){log.warn("Please manually trust the certificate in Windows:");log.warn(` 1. Double-click: ${caCertPath}`);log.warn(' 2. Click "Install Certificate"');log.warn(' 3. Select "Local Machine" and place in "Trusted Root Certification Authorities"');return!1}return!1}export async function generateCertificates(domain,verbose){try{const tlsxModule=await import("@stacksjs/tlsx"),{generateCertificate,createRootCA}=tlsxModule
|
|
5
|
+
`;if(process.platform==="win32"){log.warn("Please add this to your hosts file manually:");log.warn(`127.0.0.1 ${domain}`);log.warn(`::1 ${domain}`);log.warn(`File location: ${HOSTS_FILE}`);return!1}if(!canRunSudo()){if(verbose){log.warn(`Cannot add ${domain} to hosts file (non-interactive mode and no SUDO_PASSWORD set)`);log.info("Run `buddy setup:ssl` in a terminal or set SUDO_PASSWORD environment variable")}return!1}const sudoPassword=getSudoPassword();return new Promise((resolve)=>{let child;if(sudoPassword)child=spawn("sh",["-c",`echo '${sudoPassword}' | sudo -S sh -c "echo '${hostsEntry}' >> '${HOSTS_FILE}'"`],{stdio:["pipe","inherit","inherit"]});else child=spawn("sudo",["sh","-c",`echo '${hostsEntry}' >> "${HOSTS_FILE}"`],{stdio:"inherit"});child.on("close",(code)=>{if(code===0){if(verbose)log.success(`Added ${domain} to hosts file`);resolve(!0)}else{if(verbose){log.error(`Failed to add ${domain} to hosts file`);log.warn(`Manually add: 127.0.0.1 ${domain}`)}resolve(!1)}});child.on("error",()=>{log.error("Failed to run sudo command");resolve(!1)})})}export function sslCertificatesExist(domain){const certPath=path.join(SSL_DIR,`${domain}.crt`),keyPath=path.join(SSL_DIR,`${domain}.key`),caPath=path.join(SSL_DIR,`${domain}.ca.crt`);return fs.existsSync(certPath)&&fs.existsSync(keyPath)&&fs.existsSync(caPath)}export function isCertificateTrusted(domain){if(process.platform!=="darwin")return!0;const certPath=path.join(SSL_DIR,`${domain}.crt`);if(!fs.existsSync(certPath))return!1;try{const fingerprint=execSync(`openssl x509 -noout -fingerprint -sha256 -in "${certPath}"`,{encoding:"utf-8"}),eqIdx=fingerprint.indexOf("="),fingerprintValue=eqIdx>=0?fingerprint.slice(eqIdx+1).trim():"";if(!fingerprintValue)return!1;return execSync("security find-certificate -a -Z -p | openssl x509 -noout -fingerprint -sha256 2>/dev/null || true",{encoding:"utf-8"}).includes(fingerprintValue)}catch{return!1}}export async function trustCertificate(domain,verbose){const caCertPath=path.join(SSL_DIR,`${domain}.ca.crt`),certPath=path.join(SSL_DIR,`${domain}.crt`);if(!fs.existsSync(caCertPath)){log.error(`CA certificate not found: ${caCertPath}`);return!1}if(isCertificateTrusted(domain)){if(verbose)log.info("Certificate is already trusted, skipping trust step");return!0}if(!canRunSudo()){if(verbose){log.warn("Cannot trust certificate (non-interactive mode and no SUDO_PASSWORD set)");log.info("Run `buddy setup:ssl` in a terminal or set SUDO_PASSWORD environment variable")}return!1}const sudoPassword=getSudoPassword();if(process.platform==="darwin")return new Promise((resolve)=>{const combinedCommand=`security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain '${caCertPath}' && security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain '${certPath}'`;let child;if(sudoPassword)child=spawn("sh",["-c",`echo '${sudoPassword}' | sudo -S sh -c "${combinedCommand}"`],{stdio:["pipe","inherit","inherit"]});else child=spawn("sudo",["sh","-c",combinedCommand],{stdio:"inherit"});child.on("close",(code)=>{if(code===0){try{execSync(`security add-trusted-cert -d -r trustRoot -k ~/Library/Keychains/login.keychain-db "${certPath}" 2>/dev/null || true`)}catch{}resolve(!0)}else{if(verbose){log.warn("Could not trust certificate automatically");log.info(` sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${caCertPath}"`)}resolve(!1)}});child.on("error",()=>{log.error("Failed to run sudo command");resolve(!1)})});else if(process.platform==="linux")return new Promise((resolve)=>{const linuxCommands=`mkdir -p /usr/local/share/ca-certificates/stacks && cp '${caCertPath}' /usr/local/share/ca-certificates/stacks/ && update-ca-certificates`;let child;if(sudoPassword)child=spawn("sh",["-c",`echo '${sudoPassword}' | sudo -S sh -c "${linuxCommands}"`],{stdio:["pipe","inherit","inherit"]});else child=spawn("sudo",["sh","-c",linuxCommands],{stdio:"inherit"});child.on("close",(code)=>{if(code===0){log.success("Certificate trusted");resolve(!0)}else{log.warn("Could not trust certificate automatically");resolve(!1)}});child.on("error",()=>resolve(!1))});else if(process.platform==="win32"){log.warn("Please manually trust the certificate in Windows:");log.warn(` 1. Double-click: ${caCertPath}`);log.warn(' 2. Click "Install Certificate"');log.warn(' 3. Select "Local Machine" and place in "Trusted Root Certification Authorities"');return!1}return!1}export async function generateCertificates(domain,verbose){try{const tlsxModule=await import("@stacksjs/tlsx"),{generateCertificate,createRootCA}=tlsxModule;if(!fs.existsSync(SSL_DIR))fs.mkdirSync(SSL_DIR,{recursive:!0});const config={domain,hostCertCN:domain,basePath:SSL_DIR,caCertPath:path.join(SSL_DIR,`${domain}.ca.crt`),certPath:path.join(SSL_DIR,`${domain}.crt`),keyPath:path.join(SSL_DIR,`${domain}.key`),altNameIPs:["127.0.0.1","::1"],altNameURIs:["localhost",domain],organizationName:"Stacks Development",countryName:"US",stateName:"California",localityName:"Playa Vista",commonName:domain,validityDays:825,verbose:verbose||!1,subjectAltNames:[{type:2,value:domain},{type:2,value:`*.${domain.split(".").slice(1).join(".")}`},{type:2,value:"localhost"},{type:2,value:"*.localhost"}]};if(verbose)log.info("Generating Root CA certificate...");const caCert=await createRootCA(config);if(verbose)log.info(`Generating host certificate for ${domain}...`);const hostCert=await generateCertificate({...config,rootCA:{certificate:caCert.certificate,privateKey:caCert.privateKey}});fs.writeFileSync(config.caCertPath,caCert.certificate);fs.writeFileSync(config.certPath,hostCert.certificate);fs.writeFileSync(config.keyPath,hostCert.privateKey);if(verbose)log.success(`SSL certificates generated for ${domain}`);return!0}catch(error){log.error(`Failed to generate certificates: ${error}`);return!1}}export async function setupSSL(options={}){const domain=options.domain||process.env.APP_URL?.replace(/^https?:\/\//,"")||"stacks.localhost";if(domain==="localhost"||domain.startsWith("localhost:")){log.info("Skipping SSL setup for localhost");return!0}let success=!0;if(!options.skipHosts){if(!await addDomainToHosts(domain,options.verbose)&&options.verbose)log.warn("Hosts file not updated - you may need to add the entry manually")}if(!sslCertificatesExist(domain)){if(options.verbose)log.info("Generating SSL certificates...");if(!await generateCertificates(domain,options.verbose)){log.error("Failed to generate SSL certificates");return!1}}if(!options.skipTrust&&!isCertificateTrusted(domain)){if(!await trustCertificate(domain,options.verbose)){log.warn("Certificate not trusted - you may see browser warnings");success=!1}}return success}export function isSSLSetupComplete(domain){return isDomainInHosts(domain)&&sslCertificatesExist(domain)}
|
package/dist/upgrade/binary.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import process from"node:process";import{runCommand}from"@stacksjs/cli";import{log}from"@stacksjs/logging";import{path}from"@stacksjs/path";import{ExitCode}from"@stacksjs/types";import{fs,storage}from"@stacksjs/storage";const source=path.buddyPath("dist/stacks"),destination=path.homeDir(".stacks/bin/stacks"),destinationDir=path.homeDir(".stacks/bin/");log.info("Upgrading `stacks`...");const result=await runCommand("bun compile.ts",{cwd:path.buddyPath()});if(result.isErr){log.error("There was an error compiling the binary",result.error);process.exit(ExitCode.FatalError)}if(await storage.exists(source))try{log.info("Updating Binary...");log.info(`Source: ${source}`);log.info(`Destination: ${destination}`);await fs.
|
|
1
|
+
import process from"node:process";import{runCommand}from"@stacksjs/cli";import{log}from"@stacksjs/logging";import{path}from"@stacksjs/path";import{ExitCode}from"@stacksjs/types";import{fs,storage}from"@stacksjs/storage";const source=path.buddyPath("dist/stacks"),destination=path.homeDir(".stacks/bin/stacks"),destinationDir=path.homeDir(".stacks/bin/");log.info("Upgrading `stacks`...");const result=await runCommand("bun compile.ts",{cwd:path.buddyPath()});if(result.isErr){log.error("There was an error compiling the binary",result.error);process.exit(ExitCode.FatalError)}if(await storage.exists(source))try{log.info("Updating Binary...");log.info(`Source: ${source}`);log.info(`Destination: ${destination}`);await fs.promises.mkdir(destinationDir,{recursive:!0});await fs.promises.copyFile(source,destination);await fs.promises.unlink(source);log.success("Binary Latest Version Is Used")}catch(err){log.error(err);process.exit(ExitCode.FatalError)}else{log.error(`Binary source not found: ${source}`);process.exit(ExitCode.FatalError)}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/actions",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.73.0",
|
|
6
6
|
"description": "The Stacks actions.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -60,36 +60,37 @@
|
|
|
60
60
|
"prepublishOnly": "bun run build"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@stacksjs/config": "0.
|
|
63
|
+
"@stacksjs/config": "0.73.0",
|
|
64
64
|
"@stacksjs/bumpx": "^0.2.6",
|
|
65
65
|
"@stacksjs/bunpress": "^0.2.8",
|
|
66
66
|
"@stacksjs/logsmith": "^0.2.3",
|
|
67
|
-
"@stacksjs/registry": "0.
|
|
68
|
-
"@stacksjs/stx": "^0.2.
|
|
69
|
-
"@stacksjs/ts-cloud": "^0.12.
|
|
67
|
+
"@stacksjs/registry": "0.73.0",
|
|
68
|
+
"@stacksjs/stx": "^0.2.246",
|
|
69
|
+
"@stacksjs/ts-cloud": "^0.12.10",
|
|
70
70
|
"@stacksjs/ts-md": "^0.1.1"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
|
-
"@stacksjs/api": "0.
|
|
74
|
-
"@stacksjs/cli": "0.
|
|
75
|
-
"@stacksjs/database": "0.
|
|
73
|
+
"@stacksjs/api": "0.73.0",
|
|
74
|
+
"@stacksjs/cli": "0.73.0",
|
|
75
|
+
"@stacksjs/database": "0.73.0",
|
|
76
76
|
"@stacksjs/tlsx": "^0.13.2",
|
|
77
77
|
"better-dx": "^0.2.24",
|
|
78
|
-
"@stacksjs/dns": "0.
|
|
79
|
-
"@stacksjs/enums": "0.
|
|
80
|
-
"@stacksjs/env": "0.
|
|
81
|
-
"@stacksjs/error-handling": "0.
|
|
82
|
-
"@stacksjs/image": "0.
|
|
83
|
-
"@stacksjs/logging": "0.
|
|
84
|
-
"@stacksjs/path": "0.
|
|
85
|
-
"@stacksjs/security": "0.
|
|
86
|
-
"@stacksjs/server": "0.
|
|
87
|
-
"@stacksjs/cms": "0.
|
|
88
|
-
"@stacksjs/sites": "0.
|
|
89
|
-
"@stacksjs/storage": "0.
|
|
90
|
-
"@stacksjs/strings": "0.
|
|
91
|
-
"@stacksjs/utils": "0.
|
|
92
|
-
"@stacksjs/validation": "0.
|
|
78
|
+
"@stacksjs/dns": "0.73.0",
|
|
79
|
+
"@stacksjs/enums": "0.73.0",
|
|
80
|
+
"@stacksjs/env": "0.73.0",
|
|
81
|
+
"@stacksjs/error-handling": "0.73.0",
|
|
82
|
+
"@stacksjs/image": "0.73.0",
|
|
83
|
+
"@stacksjs/logging": "0.73.0",
|
|
84
|
+
"@stacksjs/path": "0.73.0",
|
|
85
|
+
"@stacksjs/security": "0.73.0",
|
|
86
|
+
"@stacksjs/server": "0.73.0",
|
|
87
|
+
"@stacksjs/cms": "0.73.0",
|
|
88
|
+
"@stacksjs/sites": "0.73.0",
|
|
89
|
+
"@stacksjs/storage": "0.73.0",
|
|
90
|
+
"@stacksjs/strings": "0.73.0",
|
|
91
|
+
"@stacksjs/utils": "0.73.0",
|
|
92
|
+
"@stacksjs/validation": "0.73.0",
|
|
93
|
+
"@stacksjs/build": "0.73.0"
|
|
93
94
|
},
|
|
94
95
|
"peerDependencies": {
|
|
95
96
|
"pickier": "^0.1.58",
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { LibraryType } from '@stacksjs/path';
|
|
2
|
-
/**
|
|
3
|
-
* Based on the config values, this method
|
|
4
|
-
* will generate the library entry points.
|
|
5
|
-
*
|
|
6
|
-
* @param type LibraryType
|
|
7
|
-
*/
|
|
8
|
-
export declare function generateLibEntry(type: LibraryType): Promise<void>;
|
|
9
|
-
export declare function createLibraryEntryPoint(type: LibraryType): Promise<void>;
|
|
10
|
-
export declare function createWebComponentLibraryEntryPoint(type?: LibraryType): Promise<void>;
|
|
11
|
-
export declare function createFunctionLibraryEntryPoint(type?: LibraryType): Promise<void>;
|
|
12
|
-
export declare function generateEntryPointData(type: LibraryType): string;
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import process from"node:process";import{library}from"@stacksjs/config";import{log}from"@stacksjs/logging";import{componentsPath,functionsPath,libraryEntryPath}from"@stacksjs/path";import{writeTextFile}from"@stacksjs/storage";import{kebabCase}from"@stacksjs/strings";import{ExitCode}from"@stacksjs/types";import{determineResetPreset}from"@stacksjs/utils";export async function generateLibEntry(type){await createLibraryEntryPoint(type)}export async function createLibraryEntryPoint(type){if(type==="web-components")await createWebComponentLibraryEntryPoint();if(type==="functions")await createFunctionLibraryEntryPoint()}export async function createWebComponentLibraryEntryPoint(type="web-components"){log.info("Ensuring Web Component Library Entry Point...");await writeTextFile({path:libraryEntryPath(type),data:generateEntryPointData(type)}).catch((err)=>{log.error("There was an error generating the Web Component library entry point",err);process.exit(ExitCode.FatalError)});log.success("Created Web Component Library Entry Point")}export async function createFunctionLibraryEntryPoint(type="functions"){log.info("Ensuring Function Library Entry Point...");await writeTextFile({path:libraryEntryPath(type),data:generateEntryPointData(type)}).catch((err)=>{log.error("There was an error generating Function Library Entry Point",err);process.exit(ExitCode.FatalError)});log.success("Created Functions Library Entry Point")}export function generateEntryPointData(type){let arr=[];if(type==="functions"){if(!library.functions?.files){log.error(Error("There are no functions defined to be built. Please check your config/library.ts file for potential adjustments"));process.exit(ExitCode.FatalError)}for(const fx of library.functions.files)if(Array.isArray(fx))arr.push(`export * as ${fx[1]} from '${functionsPath(fx[0])}'`);else arr.push(`export * from '${functionsPath(fx)}'`);return arr.join(`\r
|
|
2
|
-
`)}arr=determineResetPreset();const imports=[...arr,"import { defineCustomElement } from '@stacksjs/stx'"],declarations=[],definitions=[];if(!library.webComponents?.tags){log.error(Error("There are no components defined to be built. Please check your config/library.ts file for potential adjustments"));process.exit(ExitCode.FatalError)}for(const component of library.webComponents.tags.map((tag)=>tag.name))if(Array.isArray(component)){imports.push(`import ${component[1]} from '${componentsPath(component[0])}.stx'`);declarations.push(`const ${component[1]}CustomElement = defineCustomElement(${component[1]})`);definitions.push(`customElements.define('${kebabCase(component[1])}', ${component[1]}CustomElement)`)}else{imports.push(`import ${component} from '${componentsPath(component)}.stx'`);declarations.push(`const ${component}CustomElement = defineCustomElement(${component})`);definitions.push(`customElements.define('${kebabCase(component)}', ${component}CustomElement)`)}return[...imports,...declarations,...definitions].join(`\r
|
|
3
|
-
`)}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import{library}from"@stacksjs/config";import{log}from"@stacksjs/logging";import{packageJsonPath}from"@stacksjs/path";import{writeTextFile}from"@stacksjs/storage";export async function generatePackageJson(type){let name,description,directory,keywords,config,prettyName;if(type==="web-components"){name=library.webComponents?.name;description=library.webComponents?.description;directory="components";keywords=library.webComponents?.keywords;config="web-components"}else if(type==="functions"){name=library.functions?.name;description=library.functions?.description;directory="functions";keywords=library.functions?.keywords;config="functions"}try{await writeTextFile({path:packageJsonPath(type),data:`{
|
|
2
|
-
"name": "${name}",
|
|
3
|
-
"type": "module",
|
|
4
|
-
"version": "",
|
|
5
|
-
"packageManager": "bun",
|
|
6
|
-
"description": "${description}",
|
|
7
|
-
"author": "${library.author}",
|
|
8
|
-
"license": "MIT",
|
|
9
|
-
"homepage": "https://github.com/${library.repository}/tree/main/${directory}#readme",
|
|
10
|
-
"repository": {
|
|
11
|
-
"type": "git",
|
|
12
|
-
"url": "git+https://github.com/${library.repository}.git",
|
|
13
|
-
"directory": "${directory}"
|
|
14
|
-
},
|
|
15
|
-
"bugs": {
|
|
16
|
-
"url": "https://github.com/${library.repository}/issues"
|
|
17
|
-
},
|
|
18
|
-
"keywords": ${JSON.stringify(keywords)},
|
|
19
|
-
"contributors": ${JSON.stringify(library.contributors)},
|
|
20
|
-
"exports": {
|
|
21
|
-
".": {
|
|
22
|
-
"types": "./dist/index.d.js",
|
|
23
|
-
"require": "./dist/index.cjs",
|
|
24
|
-
"import": "./dist/index.js"
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
"main": "dist/index.cjs",
|
|
28
|
-
"module": "dist/index.js",
|
|
29
|
-
"types": "dist/index.d.ts",
|
|
30
|
-
"files": [
|
|
31
|
-
"dist",
|
|
32
|
-
"README.md"
|
|
33
|
-
],
|
|
34
|
-
"scripts": {
|
|
35
|
-
"build": "vite build -c ../build/${config}.ts",
|
|
36
|
-
"prepublishOnly": "bun run build"
|
|
37
|
-
},
|
|
38
|
-
"devDependencies": {
|
|
39
|
-
"stacks": "workspace:*"
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
`});if(type==="web-components")prettyName="Web Component library";else if(type==="functions")prettyName="Function Library";log.success(`Created ${prettyName} package.json file`)}catch(err){log.error(`There was an error creating the ${prettyName} package.json`,err)}}
|