Convert import/export to require/module.exports;Window conditions

Add conditions for whether to use certain things if run from server
This commit is contained in:
Robbie Antenesse 2020-09-20 17:19:12 -06:00
parent 2cfa08abe3
commit 3bfaf5f3df
27 changed files with 217 additions and 134 deletions

View File

@ -1,4 +1,4 @@
export const appListeners = (app, state, emitter) => { const appListeners = (app, state, emitter) => {
emitter.on(state.events.DOMCONTENTLOADED, () => { emitter.on(state.events.DOMCONTENTLOADED, () => {
emitter.emit(state.events.DOMTITLECHANGE, app.siteConfig.siteName); emitter.emit(state.events.DOMTITLECHANGE, app.siteConfig.siteName);
@ -53,10 +53,14 @@ export const appListeners = (app, state, emitter) => {
}).then(result => callback(result)); }).then(result => callback(result));
}); });
state.i18n.fetchLocaleUI().then(() => { if (typeof window !== 'undefined') {
app.checkIfLoggedIn(state).then(isLoggedIn => { state.i18n.fetchLocaleUI().then(() => {
emitter.emit(state.events.RENDER); // This should hopefully only run once after the DOM is loaded. It prevents routing issues where 'render' hasn't been defined yet app.checkIfLoggedIn(state).then(isLoggedIn => {
emitter.emit(state.events.RENDER); // This should hopefully only run once after the DOM is loaded. It prevents routing issues where 'render' hasn't been defined yet
});
}); });
}) }
}); });
} }
module.exports = { appListeners };

View File

@ -1,12 +1,12 @@
import { globalView } from './views/global'; const { globalView } = require('./views/global');
import { homeView } from './views/home'; const { homeView } = require('./views/home');
import { aboutView } from './views/about'; const { aboutView } = require('./views/about');
import { loginView } from './views/login'; const { loginView } = require('./views/login');
import { searchView } from './views/search'; const { searchView } = require('./views/search');
import { shelvesView } from './views/shelves'; const { shelvesView } = require('./views/shelves');
import { errorView } from './views/404'; const { errorView } = require('./views/404');
export const appRoutes = (app) => { const appRoutes = (app) => {
app.route('/', (state, emit) => globalView(state, emit, homeView)); app.route('/', (state, emit) => globalView(state, emit, homeView));
app.route('/about', (state, emit) => globalView(state, emit, aboutView)); app.route('/about', (state, emit) => globalView(state, emit, aboutView));
@ -21,3 +21,5 @@ export const appRoutes = (app) => {
app.route('/404', (state, emit) => globalView(state, emit, errorView)); app.route('/404', (state, emit) => globalView(state, emit, errorView));
} }
module.exports = { appRoutes };

View File

@ -1,11 +1,15 @@
import { I18n } from "./i18n"; const { I18n } = require("./i18n");
export const appState = (app, state, emitter) => { const appState = (app, state, emitter) => {
state.events.SET_LANGUAGE = 'setLanguage'; state.events.SET_LANGUAGE = 'setLanguage';
state.events.ADD_TO_SHELF = 'addToShelf'; state.events.ADD_TO_SHELF = 'addToShelf';
state.language = app.getSettingsItem('lang') ? app.getSettingsItem('lang') : (navigator.language || navigator.userLanguage).split('-')[0]; if (typeof window !== 'undefined') {
state.language = app.getSettingsItem('lang') ? app.getSettingsItem('lang') : (window.navigator.language || window.navigator.userLanguage).split('-')[0];
state.isLoggedIn = false;
state.i18n = new I18n(state); // Global I18n class passed to all views
}
state.viewStates = {}; state.viewStates = {};
state.isLoggedIn = false; }
state.i18n = new I18n(state); // Global I18n class passed to all views
} module.exports = { appState };

View File

@ -1,6 +1,6 @@
export const appUtilities = (app) => { const appUtilities = (app) => {
app.getSettingsItem = settingsKey => { app.getSettingsItem = settingsKey => {
let savedSettings = window.localStorage.getItem('settings'); let savedSettings = typeof window !== 'undefined' && window.localStorage.getItem('settings');
if (savedSettings) { if (savedSettings) {
savedSettings = JSON.parse(savedSettings); savedSettings = JSON.parse(savedSettings);
if (typeof savedSettings[settingsKey] !== 'undefined') { if (typeof savedSettings[settingsKey] !== 'undefined') {
@ -10,6 +10,8 @@ export const appUtilities = (app) => {
return null; return null;
} }
app.setSettingsItem = (settingsKey, value) => { app.setSettingsItem = (settingsKey, value) => {
if (typeof window === 'undefined') return null;
let savedSettings = window.localStorage.getItem('settings'); let savedSettings = window.localStorage.getItem('settings');
if (savedSettings) { if (savedSettings) {
savedSettings = JSON.parse(savedSettings); savedSettings = JSON.parse(savedSettings);
@ -21,6 +23,7 @@ export const appUtilities = (app) => {
} }
app.checkIfLoggedIn = (appState) => { app.checkIfLoggedIn = (appState) => {
if (typeof window === 'undefined') return false;
return fetch('/api/account/validate', { method: 'post' }) return fetch('/api/account/validate', { method: 'post' })
.then(response => response.json()) .then(response => response.json())
.then(response => { .then(response => {
@ -34,4 +37,6 @@ export const appUtilities = (app) => {
return true; return true;
}); });
} }
} }
module.exports = { appUtilities };

View File

@ -1,4 +1,4 @@
export class I18n { class I18n {
constructor(appState) { constructor(appState) {
this.appState = appState; this.appState = appState;
this.availableLanguages = null; this.availableLanguages = null;
@ -56,6 +56,7 @@ export class I18n {
}, Object.assign({}, language)); }, Object.assign({}, language));
if (translation === false) { if (translation === false) {
console.log(this);
if (language.locale !== this.default.locale) { if (language.locale !== this.default.locale) {
console.warn(`The translation for "${target}" is not set in the ${this.language.locale} locale. Using ${this.default.name} (${this.default.locale}) instead.`); console.warn(`The translation for "${target}" is not set in the ${this.language.locale} locale. Using ${this.default.name} (${this.default.locale}) instead.`);
return this.translate(target, true); return this.translate(target, true);
@ -71,3 +72,5 @@ export class I18n {
return this.translate(translation); return this.translate(translation);
} }
} }
module.exports = { I18n };

View File

@ -1,33 +1,43 @@
import 'babel-polyfill'; require('babel-polyfill');
import choo from 'choo'; const choo = require('choo');
import config from './config.json'; const config = require('./config.json');
import { appRoutes } from './appRoutes'; const { appRoutes } = require('./appRoutes');
import { appListeners } from './appListeners'; const { appListeners } = require('./appListeners');
import { appState } from './appState.js'; const { appState } = require('./appState.js');
import { appUtilities } from './appUtilities.js'; const { appUtilities } = require('./appUtilities.js');
const app = choo(); function frontend() {
const app = choo();
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
// Only runs in development and will be stripped from production build. // Only runs in development and will be stripped from production build.
app.use(require('choo-devtools')()); // Exposes `choo` to the console for debugging! app.use(require('choo-devtools')()); // Exposes `choo` to the console for debugging!
}
app.use((state, emitter) => {
app.siteConfig = config;
appUtilities(app);
});
app.use((state, emitter) => {
appState(app, state);
// Listeners
appListeners(app, state, emitter);
});
// Routes
appRoutes(app);
app.mount('body'); // Overwrite the `<body>` tag with the content of the Choo app
return app;
} }
app.use((state, emitter) => { if (typeof window !== 'undefined') {
app.siteConfig = config; frontend();
appUtilities(app); }
});
app.use((state, emitter) => { module.exports = frontend;
appState(app, state);
// Listeners
appListeners(app, state, emitter);
});
// Routes
appRoutes(app);
app.mount('body'); // Overwrite the `<body>` tag with the content of the Choo app

View File

@ -1,6 +1,6 @@
import html from 'choo/html'; const html = require('choo/html');
export const errorView = (state, emit, i18n) => { const errorView = (state, emit, i18n) => {
return html`<section class="error card"> return html`<section class="error card">
<header> <header>
<h1>${i18n.__('404.header')}</h1> <h1>${i18n.__('404.header')}</h1>
@ -9,4 +9,6 @@ export const errorView = (state, emit, i18n) => {
<h2>${i18n.__('404.subheader')}</h2> <h2>${i18n.__('404.subheader')}</h2>
</footer> </footer>
</section>`; </section>`;
} }
module.exports = { errorView };

View File

@ -1,6 +1,6 @@
import html from 'choo/html'; const html = require('choo/html');
export const aboutView = (state, emit, i18n) => { const aboutView = (state, emit, i18n) => {
const content = html`<section class="content"><i class="icon-loading animate-spin"></i></section>`; const content = html`<section class="content"><i class="icon-loading animate-spin"></i></section>`;
const community = html`<section class="content"></section>`; const community = html`<section class="content"></section>`;
@ -19,4 +19,6 @@ export const aboutView = (state, emit, i18n) => {
content, content,
community, community,
]; ];
} }
module.exports = { aboutView };

View File

@ -1,4 +1,4 @@
export class ViewController { class ViewController {
constructor(state, i18n, viewName, defaultState = {}) { constructor(state, i18n, viewName, defaultState = {}) {
// Store the global app state so it's accessible but out of the way. // Store the global app state so it's accessible but out of the way.
this.appState = state; this.appState = state;
@ -15,4 +15,6 @@ export class ViewController {
get isLoggedIn () { get isLoggedIn () {
return this.appState.isLoggedIn; return this.appState.isLoggedIn;
} }
} }
module.exports = { ViewController };

View File

@ -1,10 +1,23 @@
import html from 'choo/html'; const html = require('choo/html');
import headerImage from '../../dev/images/header.png'; let headerImage;
export const globalView = (state, emit, view) => { if (typeof window !== 'undefined') {
// Make Parcel bundler process image
headerImage = require('../../dev/images/header.png');
} else {
// Make server get processed image path
const fs = require('fs');
const path = require('path');
const publicPath = path.resolve('public');
const publicFiles = fs.readdirSync(publicPath);
const headerImageFileName = publicFiles.find(fileName => /header\..+?\.png/.test(fileName));
headerImage = path.relative(publicPath, path.resolve(publicPath, headerImageFileName));
}
const globalView = (state, emit, view) => {
const { i18n } = state; const { i18n } = state;
if (i18n.needsFetch) { if (typeof window !== 'undefined' && i18n.needsFetch) {
return html`<body><i class="icon-loading animate-spin"></i></body>`; return html`<body><i class="icon-loading animate-spin"></i></body>`;
} }
// Create a wrapper for view content that includes global header/footer // Create a wrapper for view content that includes global header/footer
@ -70,4 +83,6 @@ export const globalView = (state, emit, view) => {
</nav> </nav>
</footer> </footer>
</body>`; </body>`;
} }
module.exports = { globalView };

View File

@ -1,6 +1,6 @@
import { ViewController } from '../controller'; const { ViewController } = require('../controller');
export class HomeController extends ViewController { class HomeController extends ViewController {
constructor(state, i18n) { constructor(state, i18n) {
// Super passes state, view name, and default state to ViewController, // Super passes state, view name, and default state to ViewController,
// which stores state in this.appState and the view controller's state to this.state // which stores state in this.appState and the view controller's state to this.state
@ -19,4 +19,6 @@ export class HomeController extends ViewController {
// either bind the class's 'this' instance to the method first... // either bind the class's 'this' instance to the method first...
// or use `onclick=${() => controller.submit()}` to maintain the 'this' of the class instead. // or use `onclick=${() => controller.submit()}` to maintain the 'this' of the class instead.
} }
} }
module.exports = { HomeController }

View File

@ -1,19 +1,21 @@
import html from 'choo/html'; const html = require('choo/html');
import { HomeController } from './controller'; // The controller for this view, where processing should happen. const { HomeController } = require('./controller'); // The controller for this view, where processing should happen.
import { loggedOutView } from './loggedOut'; const { loggedOutView } = require('./loggedOut');
import { loggedInView } from './loggedIn'; const { loggedInView } = require('./loggedIn');
// This is the view function that is exported and used in the view manager. // This is the view function that is exported and used in the view manager.
export const homeView = (state, emit, i18n) => { const homeView = (state, emit, i18n) => {
const controller = new HomeController(state, i18n); const controller = new HomeController(state, i18n);
// Returning an array in a view allows non-shared parent HTML elements. // Returning an array in a view allows non-shared parent HTML elements.
// This one doesn't have the problem right now, but it's good to remember. // This one doesn't have the problem right now, but it's good to remember.
return [ return [
(!controller.isLoggedIn (!controller.isLoggedIn || typeof window === 'undefined'
? loggedOutView(controller, emit) ? loggedOutView(controller, emit)
: loggedInView(controller, emit) : loggedInView(controller, emit)
), ),
]; ];
} }
module.exports = { homeView };

View File

@ -1,6 +1,6 @@
import html from 'choo/html'; const html = require('choo/html');
export const loggedInView = (homeController, emit) => { const loggedInView = (homeController, emit) => {
const { __ } = homeController.i18n; const { __ } = homeController.i18n;
return [ return [
@ -36,4 +36,6 @@ export const loggedInView = (homeController, emit) => {
</div> </div>
</section>`, </section>`,
]; ];
} }
module.exports = { loggedInView };

View File

@ -1,6 +1,6 @@
import html from 'choo/html'; const html = require('choo/html');
export const loggedOutView = (homeController, emit) => { const loggedOutView = (homeController, emit) => {
const { __ } = homeController.i18n; const { __ } = homeController.i18n;
return [ return [
@ -80,4 +80,6 @@ export const loggedOutView = (homeController, emit) => {
<a href="/login" class="large success button">${__('home.logged_out.join_now')}</a> <a href="/login" class="large success button">${__('home.logged_out.join_now')}</a>
</section>`, </section>`,
]; ];
} }
module.exports = { loggedOutView };

View File

@ -1,6 +1,6 @@
import { ViewController } from '../controller'; const { ViewController } = require('../controller');
export class LoginController extends ViewController { class LoginController extends ViewController {
constructor(state, emit, i18n) { constructor(state, emit, i18n) {
// Super passes state, view name, and default state to ViewController, // Super passes state, view name, and default state to ViewController,
// which stores state in this.appState and the view controller's state to this.state // which stores state in this.appState and the view controller's state to this.state
@ -178,4 +178,6 @@ export class LoginController extends ViewController {
this.clearCreateAccountForm(); this.clearCreateAccountForm();
}) })
} }
} }
module.exports = { LoginController };

View File

@ -1,8 +1,8 @@
import html from 'choo/html'; const html = require('choo/html');
import { LoginController } from './controller'; const { LoginController } = require('./controller');
export const loginView = (state, emit, i18n) => { const loginView = (state, emit, i18n) => {
const controller = new LoginController(state, emit, i18n); const controller = new LoginController(state, emit, i18n);
const { __ } = controller.i18n; const { __ } = controller.i18n;
@ -164,4 +164,6 @@ export const loginView = (state, emit, i18n) => {
</div> </div>
</section>`; </section>`;
} }
module.exports = { loginView };

View File

@ -1,6 +1,6 @@
import html from 'choo/html'; const html = require('choo/html');
export const modal = (modalId, controller, contentHTML, options = {}) => { const modal = (modalId, controller, contentHTML, options = {}) => {
/* Options: /* Options:
* controller <class>: Pass the controller class with state; Requires get/set for openModal in state. * controller <class>: Pass the controller class with state; Requires get/set for openModal in state.
* buttonHTML <choo/html>: Displayed in place of the default button to open the modal * buttonHTML <choo/html>: Displayed in place of the default button to open the modal
@ -76,4 +76,6 @@ export const modal = (modalId, controller, contentHTML, options = {}) => {
</article> </article>
</div>`, </div>`,
]; ];
} }
module.exports = { modal };

View File

@ -1,8 +1,8 @@
import html from 'choo/html'; const html = require('choo/html');
import { starRating } from './starRating'; const { starRating } = require('./starRating');
export const reviewCard = (controller, review) => { const reviewCard = (controller, review) => {
const { __ } = controller.i18n; const { __ } = controller.i18n;
return html`<article class="card"> return html`<article class="card">
@ -26,4 +26,6 @@ export const reviewCard = (controller, review) => {
</span> </span>
</footer> </footer>
</article>`; </article>`;
} }
module.exports = { reviewCard };

View File

@ -1,6 +1,6 @@
import html from 'choo/html'; const html = require('choo/html');
export const starRating = (rating) => { const starRating = (rating) => {
const wholeStars = Math.floor(rating); const wholeStars = Math.floor(rating);
const hasPartial = rating - wholeStars > 0; const hasPartial = rating - wholeStars > 0;
const emptyStars = 5 - wholeStars - (hasPartial ? 1 : 0); const emptyStars = 5 - wholeStars - (hasPartial ? 1 : 0);
@ -16,4 +16,6 @@ export const starRating = (rating) => {
} }
return stars; return stars;
} }
module.exports = { starRating };

View File

@ -1,7 +1,7 @@
import { ViewController } from '../controller'; const { ViewController } = require('../controller');
import { ShelvesController } from '../shelves/controller'; const { ShelvesController } = require('../shelves/controller');
export class SearchController extends ViewController { class SearchController extends ViewController {
constructor(state, emit, i18n) { constructor(state, emit, i18n) {
// Super passes state, view name, and default state to ViewController, // Super passes state, view name, and default state to ViewController,
// which stores state in this.appState and the view controller's state to this.state // which stores state in this.appState and the view controller's state to this.state
@ -155,4 +155,6 @@ export class SearchController extends ViewController {
this.emit(RENDER); this.emit(RENDER);
}); });
} }
} }
module.exports = { SearchController };

View File

@ -1,11 +1,11 @@
import html from 'choo/html'; const html = require('choo/html');
import { SearchController } from './controller'; // The controller for this view, where processing should happen. const { SearchController } = require('./controller'); // The controller for this view, where processing should happen.
import { resultDetails } from './resultDetails'; const { resultDetails } = require('./resultDetails');
import { modal } from '../partials/modal'; const { modal } = require('../partials/modal');
// This is the view function that is exported and used in the view manager. // This is the view function that is exported and used in the view manager.
export const searchView = (state, emit, i18n) => { const searchView = (state, emit, i18n) => {
const controller = new SearchController(state, emit, i18n); const controller = new SearchController(state, emit, i18n);
const { __ } = controller.i18n; const { __ } = controller.i18n;
@ -156,4 +156,6 @@ export const searchView = (state, emit, i18n) => {
} }
</section>`, </section>`,
]; ];
} }
module.exports = { searchView };

View File

@ -1,10 +1,10 @@
import html from 'choo/html'; const html = require('choo/html');
import { reviewCard } from '../partials/reviewCard'; const { reviewCard } = require('../partials/reviewCard');
import { starRating } from '../partials/starRating'; const { starRating } = require('../partials/starRating');
import { modal } from '../partials/modal'; const { modal } = require('../partials/modal');
export const resultDetails = (searchController, result, emit = () => {}) => { const resultDetails = (searchController, result, emit = () => {}) => {
const { __ } = searchController.i18n; const { __ } = searchController.i18n;
const source = result.sources[0]; const source = result.sources[0];
const modalId = `result_${source.uri}`; const modalId = `result_${source.uri}`;
@ -120,4 +120,6 @@ export const resultDetails = (searchController, result, emit = () => {}) => {
headerText: result.name, headerText: result.name,
onShow, onShow,
}); });
} }
module.exports = { resultDetails };

View File

@ -1,6 +1,6 @@
import { ViewController } from '../controller'; const { ViewController } = require('../controller');
export class ShelvesController extends ViewController { class ShelvesController extends ViewController {
constructor(state, i18n) { constructor(state, i18n) {
// Super passes state, view name, and default state to ViewController, // Super passes state, view name, and default state to ViewController,
// which stores state in this.appState and the view controller's state to this.state // which stores state in this.appState and the view controller's state to this.state
@ -42,4 +42,6 @@ export class ShelvesController extends ViewController {
this.state.loadedShelves[this.targetShelf] = shelf; this.state.loadedShelves[this.targetShelf] = shelf;
}); });
} }
} }
module.exports = { ShelvesController };

View File

@ -1,11 +1,11 @@
import html from 'choo/html'; const html = require('choo/html');
import { ShelvesController } from './controller'; // The controller for this view, where processing should happen. const { ShelvesController } = require('./controller'); // The controller for this view, where processing should happen.
import { shelfView } from './shelf'; const { shelfView } = require('./shelf');
import { userShelvesView } from './userShelves'; const { userShelvesView } = require('./userShelves');
// This is the view function that is exported and used in the view manager. // This is the view function that is exported and used in the view manager.
export const shelvesView = (state, emit, i18n) => { const shelvesView = (state, emit, i18n) => {
const controller = new ShelvesController(state, i18n); const controller = new ShelvesController(state, i18n);
// Returning an array in a view allows non-shared parent HTML elements. // Returning an array in a view allows non-shared parent HTML elements.
@ -16,4 +16,6 @@ export const shelvesView = (state, emit, i18n) => {
: userShelvesView(controller, emit) : userShelvesView(controller, emit)
), ),
]; ];
} }
module.exports = { shelvesView };

View File

@ -1,9 +1,9 @@
import html from 'choo/html'; const html = require('choo/html');
import { starRating } from '../partials/starRating'; const { starRating } = require('../partials/starRating');
import { modal } from '../partials/modal'; const { modal } = require('../partials/modal');
export const shelfView = (shelvesController, emit) => { const shelfView = (shelvesController, emit) => {
const { __ } = shelvesController.i18n; const { __ } = shelvesController.i18n;
if (shelvesController.targetShelf === null) { if (shelvesController.targetShelf === null) {
@ -107,4 +107,6 @@ export const shelfView = (shelvesController, emit) => {
})} })}
</section>`, </section>`,
]; ];
} }
module.exports = { shelfView };

View File

@ -1,8 +1,8 @@
import html from 'choo/html'; const html = require('choo/html');
import { modal } from '../../partials/modal'; const { modal } = require('../../partials/modal');
export const editModal = (shelf, shelvesController) => { const editModal = (shelf, shelvesController) => {
const { __ } = shelvesController.i18n; const { __ } = shelvesController.i18n;
const modalId = `editShelf${shelf.id}`; const modalId = `editShelf${shelf.id}`;
@ -26,4 +26,6 @@ export const editModal = (shelf, shelvesController) => {
</label> </label>
</footer>`, </footer>`,
}); });
} }
module.exports = { editModal };

View File

@ -1,7 +1,7 @@
import html from 'choo/html'; const html = require('choo/html');
import { editModal } from './editModal'; const { editModal } = require('./editModal');
export const userShelvesView = (shelvesController, emit) => { const userShelvesView = (shelvesController, emit) => {
const { __ } = shelvesController.i18n; const { __ } = shelvesController.i18n;
if (!shelvesController.isLoggedIn) { if (!shelvesController.isLoggedIn) {
@ -52,4 +52,6 @@ export const userShelvesView = (shelvesController, emit) => {
})} })}
</section>`, </section>`,
]; ];
} }
module.exports = { userShelvesView };