Skip to content

Commit

Permalink
sdk: export more types, add showSidebar option and vm.editor.setCurre…
Browse files Browse the repository at this point in the history
…ntFile (#1837)

* fix vm.editor.getUrl returning an object and not a string
* improve types of the RDC class
* export all types from src/interfaces.ts
* refactor URL param generation
* add showSidebar option
* sdk: add experimental vm.editor.setCurrentFile
* bump version to 1.7.0-alpha.3
  • Loading branch information
Florens Verschelde authored Apr 18, 2022
1 parent 6fdafac commit 07604d2
Show file tree
Hide file tree
Showing 13 changed files with 333 additions and 205 deletions.
4 changes: 2 additions & 2 deletions sdk/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@stackblitz/sdk",
"version": "1.7.0-alpha.2",
"version": "1.7.0-alpha.3",
"description": "SDK for generating and embedding StackBlitz projects.",
"main": "./bundles/sdk.js",
"module": "./bundles/sdk.m.js",
Expand Down
57 changes: 0 additions & 57 deletions sdk/src/RDC.ts

This file was deleted.

27 changes: 12 additions & 15 deletions sdk/src/connection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { VM } from './VM';
import { CONNECT_INTERVAL, CONNECT_MAX_ATTEMPTS } from './constants';
import { genID } from './helpers';
import { VM } from './vm';

const connections: Connection[] = [];

Expand All @@ -22,15 +23,13 @@ export class Connection {
};

const pingFrame = () => {
// Ping the Iframe.
this.element.contentWindow &&
this.element.contentWindow.postMessage(
{
action: 'SDK_INIT',
id: this.id,
},
'*'
);
this.element.contentWindow?.postMessage(
{
action: 'SDK_INIT',
id: this.id,
},
'*'
);
};

// Remove the listener and interval.
Expand All @@ -43,9 +42,7 @@ export class Connection {
window.addEventListener('message', listenForSuccess);
// Then, lets immediately ping the frame.
pingFrame();
// Every 500ms thereafter we'll ping until we get a response or timeout.
// Keep track of the current try # and the max #
const maxAttempts = 20;
// Keep track of the current try number
let attempts = 0;
const interval = window.setInterval(() => {
// If the VM connection is open, cleanup and return
Expand All @@ -56,7 +53,7 @@ export class Connection {
}

// If we've exceeded the max retries, fail this promise.
if (attempts >= maxAttempts) {
if (attempts >= CONNECT_MAX_ATTEMPTS) {
cleanup();
reject('Timeout: Unable to establish a connection with the StackBlitz VM');
// Remove the (now) failed connection from the connections array
Expand All @@ -70,7 +67,7 @@ export class Connection {

attempts++;
pingFrame();
}, 500);
}, CONNECT_INTERVAL);
});

connections.push(this);
Expand Down
33 changes: 33 additions & 0 deletions sdk/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Number of milliseconds between attempts to get a response from an embedded frame
*/
export const CONNECT_INTERVAL = 500;

/**
* How many times should we try to get an init response from an embedded frame
*/
export const CONNECT_MAX_ATTEMPTS = 20;

/**
* Default height attribute for iframes
*/
export const DEFAULT_FRAME_HEIGHT = 300;

/**
* Origin of the StackBlitz instance
*/
export const DEFAULT_ORIGIN = 'https://stackblitz.com';

/**
* List of supported template names.
*/
export const PROJECT_TEMPLATES = [
'angular-cli',
'create-react-app',
'html',
'javascript',
'node',
'polymer',
'typescript',
'vue',
] as const;
19 changes: 5 additions & 14 deletions sdk/src/generate.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
import { Project, EmbedOptions, OpenOptions } from './interfaces';
import type { Project, EmbedOptions, OpenOptions } from './interfaces';
import { PROJECT_TEMPLATES } from './constants';
import { embedUrl, openTarget, openUrl } from './helpers';

const SUPPORTED_TEMPLATES: Project['template'][] = [
'angular-cli',
'create-react-app',
'html',
'javascript',
'node',
'polymer',
'typescript',
'vue',
];

function createHiddenInput(name: string, value: string) {
const input = document.createElement('input');
input.type = 'hidden';
Expand All @@ -21,8 +11,9 @@ function createHiddenInput(name: string, value: string) {
}

function createProjectForm(project: Project) {
if (!SUPPORTED_TEMPLATES.includes(project.template)) {
console.warn(`Unsupported project.template: must be one of ${SUPPORTED_TEMPLATES.join(', ')}`);
if (!PROJECT_TEMPLATES.includes(project.template)) {
const names = PROJECT_TEMPLATES.map((t) => `'${t}'`).join(', ');
console.warn(`Unsupported project.template: must be one of ${names}`);
}

const isWebContainers = project.template === 'node';
Expand Down
63 changes: 7 additions & 56 deletions sdk/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { EmbedOptions, OpenOptions } from './interfaces';

const DEFAULT_ORIGIN = 'https://stackblitz.com';
const DEFAULT_FRAME_HEIGHT = '300';
import type { EmbedOptions, OpenOptions } from './interfaces';
import { DEFAULT_FRAME_HEIGHT, DEFAULT_ORIGIN } from './constants';
import { buildParams } from './params';

/**
* Pseudo-random id string for internal accounting.
Expand All @@ -12,7 +11,7 @@ export function genID() {
}

export function openUrl(route: string, options?: OpenOptions) {
return `${getOrigin(options)}${route}${buildProjectQuery(options)}`;
return `${getOrigin(options)}${route}${buildParams(options)}`;
}

export function embedUrl(route: string, options?: EmbedOptions) {
Expand All @@ -22,64 +21,16 @@ export function embedUrl(route: string, options?: EmbedOptions) {
if (options && typeof options === 'object') {
Object.assign(config, options);
}
return `${getOrigin(config)}${route}${buildProjectQuery(config)}`;
return `${getOrigin(config)}${route}${buildParams(config)}`;
}

function getOrigin(options: OpenOptions | EmbedOptions = {}) {
function getOrigin(options: OpenOptions & EmbedOptions = {}) {
if (typeof options.origin === 'string') {
return options.origin;
}
return DEFAULT_ORIGIN;
}

function buildProjectQuery(options: OpenOptions | EmbedOptions = {}) {
const params: string[] = [];

if (options.forceEmbedLayout) {
params.push('embed=1');
}

if (options.clickToLoad) {
params.push('ctl=1');
}

for (const file of Array.isArray(options.openFile) ? options.openFile : [options.openFile]) {
if (typeof file === 'string' && file.trim() !== '') {
params.push(`file=${encodeURIComponent(file.trim())}`);
}
}

if (options.view === 'preview' || options.view === 'editor') {
params.push(`view=${options.view}`);
}

if (options.theme === 'light' || options.theme === 'dark') {
params.push(`theme=${options.theme}`);
}

if (options.hideExplorer) {
params.push('hideExplorer=1');
}

if (options.hideNavigation) {
params.push('hideNavigation=1');
}

if (options.hideDevTools) {
params.push('hideDevTools=1');
}

if (
typeof options.devToolsHeight === 'number' &&
options.devToolsHeight >= 0 &&
options.devToolsHeight <= 100
) {
params.push(`devToolsHeight=${Math.round(options.devToolsHeight)}`);
}

return params.length ? `?${params.join('&')}` : '';
}

export function replaceAndEmbed(
parent: HTMLElement,
frame: HTMLIFrameElement,
Expand Down Expand Up @@ -122,7 +73,7 @@ function setFrameDimensions(frame: HTMLIFrameElement, options?: EmbedOptions) {
}

if (!frame.height) {
frame.height = DEFAULT_FRAME_HEIGHT;
frame.height = `${DEFAULT_FRAME_HEIGHT}`;
}
if (!frame.width) {
frame.setAttribute('style', 'width:100%;');
Expand Down
43 changes: 39 additions & 4 deletions sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
export type { Project, OpenOptions, EmbedOptions } from './interfaces';
export type { VM } from './VM';
import {
connect,
embedGithubProject,
embedProject,
embedProjectId,
openGithubProject,
openProject,
openProjectId,
} from './lib';

import * as StackBlitzSDK from './lib';
export default StackBlitzSDK;
// Explicitly export public types (vs using `export * from './interfaces'`),
// so that additions to interfaces don't become a part of our public API by mistake.
export type {
Project,
ProjectOptions,
ProjectDependencies,
ProjectFiles,
ProjectSettings,
ProjectTemplate,
EmbedOptions,
OpenOptions,
OpenFileOption,
UiThemeOption,
UiViewOption,
} from './interfaces';
export type { FsDiff, VM } from './vm';

// Export a single object with methods, for compatibility with UMD and CommonJS.
// Ideally we would also have named exports, but that can create incompatibilities
// with some bundlers, and microbundle doesn't support it:
// https://github.com/developit/microbundle/issues/712
export default {
connect,
embedGithubProject,
embedProject,
embedProjectId,
openGithubProject,
openProject,
openProjectId,
};
Loading

0 comments on commit 07604d2

Please sign in to comment.