Skip to content

Commit 5fb1cc8

Browse files
ef4samselikoff
authored andcommitted
Support standalone docs apps (ember-learn#409)
This makes it possible to separate your docs application from your addon's dummy app. Fixes ember-learn#383. The way it works is: - you're still required to keep both the addon and the docs app in the same repo (so we don't need to worry about separately versioning each) - but they can be peers or subdirectories of each other - in your docs application you add ember-cli-addon-docs - in your docs application you must set the `documentingAddonAt` option to point at the path of the actual addon. As a side-effect of refactoring the way "Edit this page" links work, I think I also fixed them for API-docs pages in all addon docs sites (not just ones using this new architecture).
1 parent 7574272 commit 5fb1cc8

File tree

20 files changed

+123
-50
lines changed

20 files changed

+123
-50
lines changed

addon/adapters/-addon-docs.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import DS from 'ember-data';
2-
import config from 'dummy/config/environment';
2+
import config from 'ember-get-config';
33
import { inject as service } from '@ember/service';
44

55
export default DS.Adapter.extend({

addon/components/api/x-class/component.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { computed } from '@ember/object';
33
import { or } from '@ember/object/computed';
44
import { capitalize } from '@ember/string';
55
import { memberFilter } from '../../../utils/computed';
6-
import config from 'dummy/config/environment';
6+
import config from 'ember-get-config';
77

88
const { showImportPaths } = config['ember-cli-addon-docs'];
99

addon/components/api/x-section/component.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Component from '@ember/component';
22
import { computed } from '@ember/object';
33
import layout from './template';
4-
import config from 'dummy/config/environment';
4+
import config from 'ember-get-config';
55

66
const { showImportPaths } = config['ember-cli-addon-docs'];
77

addon/components/docs-header/component.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Component from '@ember/component';
22
import layout from './template';
3-
import config from 'dummy/config/environment';
3+
import config from 'ember-get-config';
44
import { computed } from '@ember/object';
55
import { classify } from '@ember/string';
66
import { addonLogo, addonPrefix } from 'ember-cli-addon-docs/utils/computed';

addon/components/docs-header/search-box/component.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import layout from './template';
33
import { EKMixin, keyUp } from 'ember-keyboard';
44
import { on } from '@ember/object/evented';
55
import { task } from 'ember-concurrency';
6-
import config from 'dummy/config/environment';
6+
import config from 'ember-get-config';
77
import { inject as service } from '@ember/service';
88
import { formElementHasFocus } from 'ember-cli-addon-docs/keyboard-config';
99

@@ -18,7 +18,7 @@ export default Component.extend(EKMixin, {
1818
query: null,
1919

2020
keyboardActivated: true,
21-
21+
2222
didInsertElement() {
2323
this._super();
2424

addon/components/docs-header/search-results/component.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { EKMixin, keyUp, keyDown } from 'ember-keyboard';
55
import { on } from '@ember/object/evented';
66
import { computed } from '@ember/object';
77
import { task } from 'ember-concurrency';
8-
import config from 'dummy/config/environment';
8+
import config from 'ember-get-config';
99

1010
const projectName = config['ember-cli-addon-docs'].projectName;
1111

@@ -20,7 +20,7 @@ export default Component.extend(EKMixin, {
2020
selectedIndex: null,
2121

2222
keyboardActivated: true,
23-
23+
2424
didInsertElement() {
2525
this._super();
2626

addon/components/docs-header/version-selector/component.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Component from '@ember/component';
22
import { inject as service } from '@ember/service';
33
import layout from './template';
44
import { reads } from '@ember/object/computed';
5-
// import config from 'dummy/config/environment';
5+
// import config from 'ember-get-config';
66
import { computed } from '@ember/object';
77
import { A } from '@ember/array';
88
import { getOwner } from '@ember/application';

addon/components/docs-hero/component.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Component from '@ember/component';
22
import layout from './template';
33
import { addonPrefix, unprefixedAddonName } from 'ember-cli-addon-docs/utils/computed';
4-
import config from 'dummy/config/environment';
4+
import config from 'ember-get-config';
55
import { classify } from '@ember/string';
66
const { projectName, projectDescription } = config['ember-cli-addon-docs'];
77

addon/components/docs-snippet/component.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { computed } from '@ember/object';
22
import Component from '@ember/component';
33
import layout from './template';
4-
import Snippets from "dummy/snippets";
4+
import config from 'ember-get-config';
5+
import require from 'require';
56

67
/**
78
A snippet component for demonstrating some code
@@ -80,8 +81,10 @@ export default Component.extend({
8081
name += '.hbs';
8182
}
8283

84+
let snippet = require(config.modulePrefix + "/snippets")[name] || "";
85+
8386
return this._unindent(
84-
(Snippets[name] || "")
87+
snippet
8588
.replace(/^(\s*\n)*/, '')
8689
.replace(/\s*$/, '')
8790
);

addon/components/docs-viewer/x-main/component.js

+25-13
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { bind } from '@ember/runloop';
44
import { computed } from '@ember/object';
55
import appFiles from 'ember-cli-addon-docs/app-files';
66
import addonFiles from 'ember-cli-addon-docs/addon-files';
7-
import config from 'dummy/config/environment';
7+
import config from 'ember-get-config';
88
import { getOwner } from '@ember/application';
99

1010
import layout from './template';
@@ -70,26 +70,38 @@ export default Component.extend({
7070
return;
7171
}
7272

73-
path = path.replace(/\./g, '/');
74-
75-
let { projectHref, projectPathInRepo, primaryBranch } = config['ember-cli-addon-docs'];
76-
let projectPath = projectPathInRepo ? `/${projectPathInRepo}/` : '/';
77-
let rootEditUrl = `${projectHref}/edit/${primaryBranch}${projectPath}`;
73+
let match = this._locateFile(path);
74+
if (match) {
75+
let { projectHref, addonPathInRepo, docsAppPathInRepo, primaryBranch } = config['ember-cli-addon-docs'];
76+
let parts = [projectHref, 'edit', primaryBranch];
77+
if (match.inTree === 'addon') {
78+
parts.push(addonPathInRepo);
79+
} else {
80+
parts.push(docsAppPathInRepo);
81+
}
82+
parts.push(match.file);
83+
return parts.filter(Boolean).join('/');
84+
}
85+
}),
7886

87+
_locateFile(path) {
88+
path = path.replace(/\./g, '/');
7989
if (path === 'docs/api/item') {
80-
let { path } = getOwner(this).lookup('route:application').paramsFor('docs.api.item');
81-
let file = addonFiles.find(f => f.match(path));
82-
90+
let { projectName } = config['ember-cli-addon-docs'];
91+
let model = getOwner(this).lookup('route:application').modelFor('docs.api.item');
92+
let filename = model.get('file').replace(new RegExp(`^${projectName}/`), '');
93+
let file = addonFiles.find(f => f.match(filename));
8394
if (file) {
84-
return `${rootEditUrl}addon/${file}`;
95+
return { file, inTree: 'addon' };
8596
}
8697
} else {
8798
let file = appFiles
8899
.filter(file => file.match(/\.(hbs|md)$/))
89100
.find(file => file.match(path));
90-
91-
return `${rootEditUrl}tests/dummy/app/${file}`;
101+
if (file) {
102+
return { file, inTree: 'app' };
103+
}
92104
}
93-
})
105+
}
94106

95107
});

addon/components/docs-viewer/x-nav/component.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { computed } from '@ember/object';
22
import { inject as service } from '@ember/service';
33
import Component from '@ember/component';
44
import layout from './template';
5-
import config from 'dummy/config/environment';
5+
import config from 'ember-get-config';
66
import { classify } from '@ember/string';
77
import { addonLogo } from 'ember-cli-addon-docs/utils/computed';
88

addon/routes/docs.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import Route from '@ember/routing/route';
2-
import config from 'dummy/config/environment';
2+
import config from 'ember-get-config';
33

44
const projectName = config['ember-cli-addon-docs'].projectName;
55

addon/services/docs-search.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Service, { inject as service } from '@ember/service';
22
import { getOwner } from '@ember/application';
33
import { computed } from '@ember/object';
44
import lunr from 'lunr';
5-
import config from 'dummy/config/environment';
5+
import config from 'ember-get-config';
66

77
const { Index, Query } = lunr;
88

addon/services/project-version.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Service, { inject as service } from '@ember/service';
22
import { getOwner } from '@ember/application';
33
import { computed } from '@ember/object';
44
import { task } from 'ember-concurrency';
5-
import config from 'dummy/config/environment';
5+
import config from 'ember-get-config';
66
import { assign } from '@ember/polyfills';
77

88
const { latestVersionName } = config['ember-cli-addon-docs'];

index.js

+72-15
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,35 @@ module.exports = {
5353
},
5454

5555
config(env, baseConfig) {
56-
let repo = this.parent.pkg.repository;
56+
let pkg = this.parent.pkg;
57+
if (this._documentingAddonAt()) {
58+
pkg = require(path.join(this._documentingAddonAt(), 'package.json'));
59+
}
60+
61+
let repo = pkg.repository;
5762
let info = require('hosted-git-info').fromUrl(repo.url || repo);
5863
let userConfig = this._readUserConfig();
5964

65+
let docsAppPathInRepo = path.relative(
66+
this._getRepoRoot(),
67+
path.join(
68+
path.resolve(path.dirname(this.project.configPath()), '..'),
69+
'app'
70+
)
71+
);
72+
73+
let addonPathInRepo = this._documentingAddonAt()
74+
? path.relative(this._getRepoRoot(), path.join(this._documentingAddonAt(), 'addon'))
75+
: path.relative(this._getRepoRoot(), path.join(this.project.root, 'addon'));
76+
6077
let config = {
6178
'ember-cli-addon-docs': {
62-
projectName: this.parent.pkg.name,
63-
projectDescription: this.parent.pkg.description,
64-
projectTag: this.parent.pkg.version,
79+
projectName: pkg.name,
80+
projectDescription: pkg.description,
81+
projectTag: pkg.version,
6582
projectHref: info && info.browse(),
66-
projectPathInRepo: path.relative(this._getRepoRoot(), this.project.root),
83+
docsAppPathInRepo,
84+
addonPathInRepo,
6785
primaryBranch: userConfig.getPrimaryBranch(),
6886
latestVersionName: LATEST_VERSION_NAME,
6987
deployVersion: 'ADDON_DOCS_DEPLOY_VERSION',
@@ -92,11 +110,24 @@ module.exports = {
92110
if (includer.parent) {
93111
throw new Error(`ember-cli-addon-docs should be in your package.json's devDependencies`);
94112
} else if (includer.name === this.project.name()) {
95-
throw new Error(`ember-cli-addon-docs only currently works with addons, not applications`);
113+
if (this._documentingAddonAt()) {
114+
// we're being used in a standalone documentation app that documents an
115+
// addon but is not that addon's dummy app.
116+
} else {
117+
throw new Error(`to use ember-cli-addon-docs in an application (as opposed to an addon) you must set documentingAddonAt`);
118+
}
96119
}
97120

98121
includer.options.includeFileExtensionInSnippetNames = includer.options.includeFileExtensionInSnippetNames || false;
99-
includer.options.snippetSearchPaths = includer.options.snippetSearchPaths || ['tests/dummy/app'];
122+
if (!includer.options.snippetSearchPaths) {
123+
if (this._documentingAddonAt()) {
124+
// we are a standalone app, so our code is here
125+
includer.options.snippetSearchPaths = ['app'];
126+
} else {
127+
// we are inside the addon, so our code is here
128+
includer.options.snippetSearchPaths = ['tests/dummy/app'];
129+
}
130+
}
100131
includer.options.snippetRegexes = Object.assign({}, {
101132
begin: /{{#(?:docs-snippet|demo.example)\sname=(?:"|')(\S+)(?:"|')/,
102133
end: /{{\/(?:docs-snippet|demo.example)}}/,
@@ -187,8 +218,8 @@ module.exports = {
187218

188219
treeForAddon(tree) {
189220
let dummyAppFiles = new FindDummyAppFiles([ this.app.trees.app ]);
190-
let addonFiles = new FindAddonFiles([ 'addon' ].filter(dir => fs.existsSync(dir)));
191-
221+
let addonToDocument = this._documentingAddon();
222+
let addonFiles = new FindAddonFiles([path.join(addonToDocument.root, 'addon')]);
192223
return this._super(new MergeTrees([ tree, dummyAppFiles, addonFiles ]));
193224
},
194225

@@ -209,17 +240,16 @@ module.exports = {
209240
},
210241

211242
postprocessTree(type, tree) {
212-
let parentAddon = this.parent.findAddonByName(this.parent.name());
213-
if (!parentAddon || type !== 'all') { return tree; }
243+
let addonToDocument = this._documentingAddon();
244+
if (!addonToDocument || type !== 'all') { return tree; }
214245

215246
let PluginRegistry = require('./lib/models/plugin-registry');
216247
let DocsCompiler = require('./lib/broccoli/docs-compiler');
217248
let SearchIndexer = require('./lib/broccoli/search-indexer');
218249

219250
let project = this.project;
220251
let docsTrees = [];
221-
222-
this.addonOptions.projects.main = this.addonOptions.projects.main || generateDefaultProject(parentAddon);
252+
this.addonOptions.projects.main = this.addonOptions.projects.main || generateDefaultProject(addonToDocument);
223253

224254
for (let projectName in this.addonOptions.projects) {
225255
let addonSourceTree = this.addonOptions.projects[projectName];
@@ -229,12 +259,12 @@ module.exports = {
229259
let docsGenerators = pluginRegistry.createDocsGenerators(addonSourceTree, {
230260
destDir: 'docs',
231261
project,
232-
parentAddon
262+
parentAddon: addonToDocument
233263
});
234264

235265
docsTrees.push(
236266
new DocsCompiler(docsGenerators, {
237-
name: projectName === 'main' ? parentAddon.name : projectName,
267+
name: projectName === 'main' ? addonToDocument.name : projectName,
238268
project
239269
})
240270
);
@@ -276,6 +306,33 @@ module.exports = {
276306
this._repoRoot = require('git-repo-info')().root;
277307
}
278308
return this._repoRoot;
309+
},
310+
311+
// returns the absolute path to the addon we're documenting when
312+
// ember-cli-addon-docs is being used by an *app* (not an addon) that has
313+
// explicitly set `documentingAddonAt`.
314+
_documentingAddonAt() {
315+
if (this._cachedDocumentingAddonAt === undefined && this.app) {
316+
if (this.app.options['ember-cli-addon-docs'] && this.app.options['ember-cli-addon-docs'].documentingAddonAt) {
317+
this._cachedDocumentingAddonAt = path.resolve(this.project.root, this.app.options['ember-cli-addon-docs'].documentingAddonAt);
318+
} else {
319+
this._cachedDocumentingAddonAt = null;
320+
}
321+
}
322+
return this._cachedDocumentingAddonAt;
323+
},
324+
325+
_documentingAddon() {
326+
let addon;
327+
if (this._documentingAddonAt()) {
328+
addon = this.project.addons.find(a => a.root === this._documentingAddonAt())
329+
if (!addon) {
330+
throw new Error(`You set documentingAddonAt to point at ${this._documentingAddonAt()} but that addon does not appear to be present in this app.`);
331+
}
332+
} else {
333+
addon = this.parent.findAddonByName(this.parent.name());
334+
}
335+
return addon;
279336
}
280337
};
281338

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"ember-data": "2.x - 3.x",
5656
"ember-fetch": "^6.7.1",
5757
"ember-fetch-adapter": "^0.4.3",
58+
"ember-get-config": "^0.2.4",
5859
"ember-href-to": "^1.15.1",
5960
"ember-keyboard": "^4.0.0",
6061
"ember-modal-dialog": "^3.0.0-beta.4",

tests/acceptance/sandbox/api/components-test.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { module, test } from 'qunit';
22
import { setupApplicationTest } from 'ember-qunit';
33
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
44
import { currentURL, visit, waitUntil } from '@ember/test-helpers';
5-
import config from 'dummy/config/environment';
5+
import config from 'ember-get-config';
66

77
import modulePage from '../../../pages/api/module';
88

@@ -58,11 +58,11 @@ module('Acceptance | Sandbox | API | components', function(hooks) {
5858

5959
module('in a nested directory within a repo', function(hooks) {
6060
hooks.beforeEach(function() {
61-
config['ember-cli-addon-docs'].projectPathInRepo = 'packages/foo-bar';
61+
config['ember-cli-addon-docs'].docsAppPathInRepo = 'packages/foo-bar/tests/dummy/app';
6262
});
6363

6464
hooks.afterEach(function() {
65-
config['ember-cli-addon-docs'].projectPathInRepo = '';
65+
config['ember-cli-addon-docs'].docsAppPathInRepo = '';
6666
});
6767

6868
test('welcome page \'Edit this page\' link is correct', async function(assert) {

tests/acceptance/version-selector-test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { module, test } from 'qunit';
22
import { setupApplicationTest } from 'ember-qunit';
33
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
44
import { visit, click } from '@ember/test-helpers';
5-
import config from 'dummy/config/environment';
5+
import config from 'ember-get-config';
66

77
module('Acceptance | Version selector test', function(hooks) {
88
setupApplicationTest(hooks);

tests/dummy/mirage/config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import config from 'dummy/config/environment';
1+
import config from 'ember-get-config';
22
const projectTag = config['ember-cli-addon-docs'].projectTag;
33

44
export default function() {

0 commit comments

Comments
 (0)