Skip to content

Commit bdaf552

Browse files
authored
Merge pull request #1138 from twilio/add-local-development-step
Adding local development scripts
2 parents 7600927 + 0e2303e commit bdaf552

File tree

3 files changed

+241
-0
lines changed

3 files changed

+241
-0
lines changed

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,44 @@ Flex Plugin Builder requires [Node.js](https://nodejs.org/). We support and reco
2828

2929
- [@twilio-labs/plugin-flex](packages/plugin-flex): The CLI tool for creating, building, testing, deploying, and managing your plugins
3030

31+
## Local Development Setup
32+
33+
For local development of the Flex Plugin Builder itself, follow these steps:
34+
35+
### Initial Setup
36+
37+
1. Clone the repository and install dependencies:
38+
```bash
39+
npm install
40+
```
41+
42+
2. Link all packages for local development:
43+
```bash
44+
npm run link-packages
45+
```
46+
47+
This command will:
48+
- Create global npm links for all packages in the `/packages` folder
49+
- Link internal dependencies between packages
50+
- Run `npm run link` in the plugin-flex package to register the CLI tool
51+
52+
### Development Workflow
53+
54+
After making any changes to the source code, rebuild the packages:
55+
56+
```bash
57+
npm run build
58+
```
59+
60+
This ensures that all TypeScript files are compiled and changes are reflected across linked packages.
61+
62+
### Development Tips
63+
64+
- The `link-packages` script only needs to be run once during initial setup
65+
- Run `npm run build` after every code change to see your changes take effect
66+
- Use `npm run test` to run all tests across packages
67+
- Individual packages can be built using `lerna run build --scope=@twilio/<package-name>`
68+
3169
## User Guide
3270

3371
Please visit [Twilio Docs](https://www.twilio.com/docs/flex/developer/plugins) for the latest docs on Plugins CLI and API.

link-packages.js

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
#!/usr/bin/env node
2+
3+
const fs = require('fs');
4+
const path = require('path');
5+
const { execSync } = require('child_process');
6+
7+
/**
8+
* Script to link all packages in the packages folder using npm link
9+
* and then link dependencies between them
10+
*/
11+
12+
const PACKAGES_DIR = path.join(__dirname, 'packages');
13+
const PLUGIN_FLEX_DIR = path.join(PACKAGES_DIR, 'plugin-flex');
14+
15+
// Color codes for better output
16+
const colors = {
17+
reset: '\x1b[0m',
18+
bright: '\x1b[1m',
19+
red: '\x1b[31m',
20+
green: '\x1b[32m',
21+
yellow: '\x1b[33m',
22+
blue: '\x1b[34m',
23+
magenta: '\x1b[35m',
24+
cyan: '\x1b[36m'
25+
};
26+
27+
function log(message, color = colors.reset) {
28+
console.log(`${color}${message}${colors.reset}`);
29+
}
30+
31+
function execCommand(command, cwd = process.cwd()) {
32+
try {
33+
log(`${colors.cyan}Executing: ${command}${colors.reset}`, colors.cyan);
34+
const result = execSync(command, {
35+
cwd,
36+
stdio: 'inherit',
37+
encoding: 'utf8'
38+
});
39+
return result;
40+
} catch (error) {
41+
log(`${colors.red}Error executing: ${command}${colors.reset}`, colors.red);
42+
log(`${colors.red}${error.message}${colors.reset}`, colors.red);
43+
throw error;
44+
}
45+
}
46+
47+
function getPackageInfo(packagePath) {
48+
const packageJsonPath = path.join(packagePath, 'package.json');
49+
if (!fs.existsSync(packageJsonPath)) {
50+
return null;
51+
}
52+
53+
try {
54+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
55+
return {
56+
name: packageJson.name,
57+
dependencies: {
58+
...packageJson.dependencies,
59+
...packageJson.devDependencies
60+
}
61+
};
62+
} catch (error) {
63+
log(`${colors.red}Error reading package.json for ${packagePath}: ${error.message}${colors.reset}`, colors.red);
64+
return null;
65+
}
66+
}
67+
68+
function getAllPackages() {
69+
const packages = [];
70+
const packageDirs = fs.readdirSync(PACKAGES_DIR, { withFileTypes: true })
71+
.filter(dirent => dirent.isDirectory())
72+
.map(dirent => dirent.name);
73+
74+
for (const dir of packageDirs) {
75+
const packagePath = path.join(PACKAGES_DIR, dir);
76+
const packageInfo = getPackageInfo(packagePath);
77+
78+
if (packageInfo) {
79+
packages.push({
80+
...packageInfo,
81+
path: packagePath,
82+
directory: dir
83+
});
84+
}
85+
}
86+
87+
return packages;
88+
}
89+
90+
function createGlobalLinks(packages) {
91+
log(`${colors.bright}${colors.yellow}Step 1: Creating global npm links for all packages...${colors.reset}`, colors.yellow);
92+
93+
for (const pkg of packages) {
94+
log(`${colors.green}Creating global link for ${pkg.name}...${colors.reset}`, colors.green);
95+
try {
96+
execCommand('npm link', pkg.path);
97+
log(`${colors.green}✓ Successfully linked ${pkg.name}${colors.reset}`, colors.green);
98+
} catch (error) {
99+
log(`${colors.red}✗ Failed to link ${pkg.name}${colors.reset}`, colors.red);
100+
// Continue with other packages
101+
}
102+
}
103+
}
104+
105+
function linkDependencies(packages) {
106+
log(`${colors.bright}${colors.yellow}Step 2: Linking package dependencies...${colors.reset}`, colors.yellow);
107+
108+
// Create a map of package names for quick lookup
109+
const packageNameMap = new Map();
110+
packages.forEach(pkg => packageNameMap.set(pkg.name, pkg));
111+
112+
for (const pkg of packages) {
113+
log(`${colors.blue}Processing dependencies for ${pkg.name}...${colors.reset}`, colors.blue);
114+
115+
const dependencies = pkg.dependencies || {};
116+
const localDependencies = [];
117+
118+
// Find dependencies that are also local packages
119+
for (const [depName, depVersion] of Object.entries(dependencies)) {
120+
if (packageNameMap.has(depName)) {
121+
localDependencies.push(depName);
122+
}
123+
}
124+
125+
if (localDependencies.length > 0) {
126+
log(`${colors.magenta} Found local dependencies: ${localDependencies.join(', ')}${colors.reset}`, colors.magenta);
127+
128+
for (const depName of localDependencies) {
129+
try {
130+
log(`${colors.cyan} Linking ${depName} to ${pkg.name}...${colors.reset}`, colors.cyan);
131+
execCommand(`npm link "${depName}"`, pkg.path);
132+
log(`${colors.green} ✓ Successfully linked ${depName} to ${pkg.name}${colors.reset}`, colors.green);
133+
} catch (error) {
134+
log(`${colors.red} ✗ Failed to link ${depName} to ${pkg.name}${colors.reset}`, colors.red);
135+
// Continue with other dependencies
136+
}
137+
}
138+
} else {
139+
log(`${colors.yellow} No local dependencies found${colors.reset}`, colors.yellow);
140+
}
141+
}
142+
}
143+
144+
function runPluginFlexLink() {
145+
log(`${colors.bright}${colors.yellow}Step 3: Running npm run link in plugin-flex package...${colors.reset}`, colors.yellow);
146+
147+
if (!fs.existsSync(PLUGIN_FLEX_DIR)) {
148+
log(`${colors.red}Error: plugin-flex directory not found at ${PLUGIN_FLEX_DIR}${colors.reset}`, colors.red);
149+
return;
150+
}
151+
152+
try {
153+
log(`${colors.green}Running npm run link in plugin-flex...${colors.reset}`, colors.green);
154+
execCommand('npm run link', PLUGIN_FLEX_DIR);
155+
log(`${colors.green}✓ Successfully ran npm run link in plugin-flex${colors.reset}`, colors.green);
156+
} catch (error) {
157+
log(`${colors.red}✗ Failed to run npm run link in plugin-flex${colors.reset}`, colors.red);
158+
throw error;
159+
}
160+
}
161+
162+
function main() {
163+
try {
164+
log(`${colors.bright}${colors.blue}Starting package linking process...${colors.reset}`, colors.blue);
165+
log(`${colors.bright}${colors.blue}Working directory: ${__dirname}${colors.reset}`, colors.blue);
166+
log(`${colors.bright}${colors.blue}Packages directory: ${PACKAGES_DIR}${colors.reset}`, colors.blue);
167+
168+
// Get all packages
169+
const packages = getAllPackages();
170+
log(`${colors.bright}${colors.green}Found ${packages.length} packages:${colors.reset}`, colors.green);
171+
packages.forEach(pkg => {
172+
log(` - ${pkg.name} (${pkg.directory})`, colors.green);
173+
});
174+
175+
// Step 1: Create global links
176+
createGlobalLinks(packages);
177+
178+
// Step 2: Link dependencies
179+
linkDependencies(packages);
180+
181+
// Step 3: Run npm run link in plugin-flex
182+
runPluginFlexLink();
183+
184+
log(`${colors.bright}${colors.green}🎉 Package linking completed successfully!${colors.reset}`, colors.green);
185+
186+
} catch (error) {
187+
log(`${colors.bright}${colors.red}❌ Package linking failed: ${error.message}${colors.reset}`, colors.red);
188+
process.exit(1);
189+
}
190+
}
191+
192+
// Run the script
193+
if (require.main === module) {
194+
main();
195+
}
196+
197+
module.exports = {
198+
getAllPackages,
199+
createGlobalLinks,
200+
linkDependencies,
201+
runPluginFlexLink
202+
};

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"clean": "lerna run clean",
2525
"coverage": "codecov",
2626
"docs": "./bin/docs-generator.js",
27+
"link-packages": "node link-packages.js",
2728
"lint": "lerna run lint",
2829
"lint:fix": "lerna run lint:fix",
2930
"preinstall": "npm install npm-force-resolutions --location=global",

0 commit comments

Comments
 (0)