1+ 'use strict' ;
2+
3+ var _extends = Object . assign || function ( target ) { for ( var i = 1 ; i < arguments . length ; i ++ ) { var source = arguments [ i ] ; for ( var key in source ) { if ( Object . prototype . hasOwnProperty . call ( source , key ) ) { target [ key ] = source [ key ] ; } } } return target ; } ;
4+
5+ function _toConsumableArray ( arr ) { if ( Array . isArray ( arr ) ) { for ( var i = 0 , arr2 = Array ( arr . length ) ; i < arr . length ; i ++ ) { arr2 [ i ] = arr [ i ] ; } return arr2 ; } else { return Array . from ( arr ) ; } }
6+
7+ var fs = require ( 'fs' ) ;
8+ var path = require ( 'path' ) ;
9+ var LZString = require ( 'lz-string' ) ;
10+ var normalizePath = require ( 'normalize-path' ) ;
11+ var map = require ( 'unist-util-map' ) ;
12+ var queryString = require ( 'query-string' ) ;
13+
14+ var DEFAULT_PROTOCOL = 'embedded-codesandbox://' ;
15+ var DEFAULT_EMBED_OPTIONS = {
16+ view : 'preview' ,
17+ hidenavigation : 1
18+ } ;
19+ var DEFAULT_GET_IFRAME = function DEFAULT_GET_IFRAME ( url ) {
20+ return '<iframe src="' + url + '" class="embedded-codesandbox" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe>' ;
21+ } ;
22+
23+ // Matches compression used in Babel and CodeSandbox REPLs
24+ // https://github.com/babel/website/blob/master/js/repl/UriUtils.js
25+ var compress = function compress ( string ) {
26+ return LZString . compressToBase64 ( string ) . replace ( / \+ / g, '-' ) // Convert '+' to '-'
27+ . replace ( / \/ / g, '_' ) // Convert '/' to '_'
28+ . replace ( / = + $ / , '' ) ;
29+ } ; // Remove ending '='
30+
31+ var ignoredFiles = new Set ( [ 'node_modules' , 'package-lock.json' , 'yarn.lock' ] ) ;
32+
33+ var getAllFiles = function getAllFiles ( dirPath ) {
34+ return fs . readdirSync ( dirPath ) . reduce ( function ( acc , file ) {
35+ if ( ignoredFiles . has ( file ) ) return acc ;
36+ var relativePath = dirPath + '/' + file ;
37+ var isDirectory = fs . statSync ( relativePath ) . isDirectory ( ) ;
38+ var additions = isDirectory ? getAllFiles ( relativePath ) : [ relativePath ] ;
39+ return [ ] . concat ( _toConsumableArray ( acc ) , _toConsumableArray ( additions ) ) ;
40+ } , [ ] ) ;
41+ } ;
42+
43+ module . exports = function ( _ref , _ref2 ) {
44+ var markdownAST = _ref . markdownAST ;
45+ var rootDirectory = _ref2 . directory ,
46+ _ref2$protocol = _ref2 . protocol ,
47+ protocol = _ref2$protocol === undefined ? DEFAULT_PROTOCOL : _ref2$protocol ,
48+ _ref2$embedOptions = _ref2 . embedOptions ,
49+ embedOptions = _ref2$embedOptions === undefined ? DEFAULT_EMBED_OPTIONS : _ref2$embedOptions ,
50+ _ref2$getIframe = _ref2 . getIframe ,
51+ getIframe = _ref2$getIframe === undefined ? DEFAULT_GET_IFRAME : _ref2$getIframe ;
52+
53+ if ( ! rootDirectory ) {
54+ throw Error ( 'Required option "directory" not specified' ) ;
55+ } else if ( ! fs . existsSync ( rootDirectory ) ) {
56+ throw Error ( 'Cannot find directory "' + rootDirectory + '"' ) ;
57+ } else if ( ! rootDirectory . endsWith ( '/' ) ) {
58+ rootDirectory += '/' ;
59+ }
60+
61+ var getDirectoryPath = function getDirectoryPath ( url ) {
62+ var directoryPath = url . replace ( protocol , '' ) ;
63+ var fullPath = path . join ( rootDirectory , directoryPath ) ;
64+ return normalizePath ( fullPath ) ;
65+ } ;
66+
67+ var getFilesList = function getFilesList ( directory ) {
68+ var packageJsonFound = false ;
69+ var folderFiles = getAllFiles ( directory ) ;
70+ var basePathRE = new RegExp ( '^' + directory + '/' ) ;
71+ var sandboxFiles = folderFiles . map ( function ( file ) {
72+ return file . replace ( basePathRE , '' ) ;
73+ } )
74+ // we ignore the package.json file as it will
75+ // be handled separately
76+ . filter ( function ( file ) {
77+ return ! file . includes ( 'package.json' ) ;
78+ } ) . map ( function ( relativePath ) {
79+ var fullFilePath = path . resolve ( __dirname , directory , relativePath ) ;
80+ var content = fs . readFileSync ( fullFilePath , 'utf-8' ) ;
81+ return {
82+ name : relativePath ,
83+ content : content
84+ } ;
85+ } ) ;
86+
87+ var workingDir = directory ;
88+ while ( ! packageJsonFound ) {
89+ // first read all files in the folder and look
90+ // for a package.json there
91+ var files = fs . readdirSync ( workingDir ) ;
92+ var packageJson = getPackageJsonFile ( files ) ;
93+ if ( packageJson ) {
94+ var fullFilePath = path . resolve ( workingDir , 'package.json' ) ;
95+ var content = fs . readFileSync ( fullFilePath , 'utf-8' ) ;
96+ sandboxFiles . push ( {
97+ name : 'package.json' ,
98+ content : content
99+ } ) ;
100+ packageJsonFound = true ;
101+ // if root folder is reached, use a fallback default
102+ // value as content, to ensure the sandbox is always working
103+ } else if ( path . resolve ( workingDir ) === path . resolve ( rootDirectory ) ) {
104+ sandboxFiles . push ( {
105+ name : 'package.json' ,
106+ content : '{ "name": "example" }'
107+ } ) ;
108+ packageJsonFound = true ;
109+ // if not present, work up the folders
110+ } else {
111+ workingDir = path . join ( workingDir , '..' ) ;
112+ }
113+ }
114+
115+ return sandboxFiles ;
116+ } ;
117+
118+ var getPackageJsonFile = function getPackageJsonFile ( fileList ) {
119+ return fileList . find ( function ( file ) {
120+ return file . includes ( 'package.json' ) ;
121+ } ) ;
122+ } ;
123+
124+ var createParams = function createParams ( files ) {
125+ var filesObj = files . reduce ( function ( prev , current ) {
126+ // parse package.json first
127+ if ( current . name . includes ( 'package.json' ) ) {
128+ prev [ current . name ] = { content : JSON . parse ( current . content ) } ;
129+ } else {
130+ prev [ current . name ] = { content : current . content } ;
131+ }
132+ return prev ;
133+ } , { } ) ;
134+ var params = {
135+ files : filesObj
136+ } ;
137+ return compress ( JSON . stringify ( params ) ) ;
138+ } ;
139+
140+ var getUrlParts = function getUrlParts ( url ) {
141+ var splitUrl = url . split ( '?' ) ;
142+ return {
143+ base : splitUrl [ 0 ] ,
144+ query : queryString . parse ( splitUrl [ 1 ] )
145+ } ;
146+ } ;
147+
148+ var convertNodeToEmbedded = function convertNodeToEmbedded ( node , params ) {
149+ var options = arguments . length > 2 && arguments [ 2 ] !== undefined ? arguments [ 2 ] : { } ;
150+
151+ delete node . children ;
152+ delete node . position ;
153+ delete node . title ;
154+ delete node . url ;
155+
156+ // merge the overriding options with the plugin one
157+ var mergedOptions = _extends ( { } , embedOptions , options ) ;
158+ var encodedEmbedOptions = encodeURIComponent ( queryString . stringify ( mergedOptions ) ) ;
159+ var sandboxUrl = 'https://codesandbox.io/api/v1/sandboxes/define?embed=1¶meters=' + params + '&query=' + encodedEmbedOptions ;
160+ var embedded = getIframe ( sandboxUrl ) ;
161+
162+ node . type = 'html' ;
163+ node . value = embedded ;
164+ } ;
165+
166+ map ( markdownAST , function ( node , index , parent ) {
167+ if ( node . type === 'link' && node . url . startsWith ( protocol ) ) {
168+ // split the url in base and query to allow user
169+ // to customise embedding options on a per-node basis
170+ var url = getUrlParts ( node . url ) ;
171+ // get all files in the folder and generate
172+ // the embeddeing parameters
173+ var dir = getDirectoryPath ( url . base ) ;
174+ var files = getFilesList ( dir ) ;
175+ var params = createParams ( files ) ;
176+ convertNodeToEmbedded ( node , params , url . query ) ;
177+ }
178+
179+ return node ;
180+ } ) ;
181+
182+ return markdownAST ;
183+ } ;
0 commit comments